# Welcome

Some information about me.

My self Amanullah Al Menan. I’m a professional Fivem Developer. I’m working on Fivem server since 3 years. I started my journey as a freelancer on Fiverr & worked there for a year and earned lot’s of positive review and respect. Next step was creating a discord community with my supporters to provide them necessary support for my scripts. Now I can make, modify & optimize any kind of script for Fivem server.

Tebex: [https://menanak47.tebex.io](https://menanak47.tebex.io/)\
YouTube:  <https://www.youtube.com/menanak47>\
Discord: <https://discord.gg/menanak47>


# FAQ

You will get some quick answer in this section.

### You lack the required entitlement

Make sure you are using server key from the same keymaster where you downloaded your scripts from.

### Error parsing script / Failed to load script

1. Your server artifacts are likely outdated. Update your server to version 5181 or above.
2. If you are using FTP then upload the script with WinScp

### Failed to verify protected resource

1. If you are using FTP then upload the script with WinScp
2. If windows then download the script again from keymaster & unzip with WinRaR or 7zip


# ak47\_lib

**ak47\_lib** is a unified compatibility layer designed to support multiple FiveM frameworks (ESX, QBCore, QBX) and various third-party resources (Inventory, Target, Fuel, etc.) through a single, consistent API. This allows developers to write scripts that work across different server setups without writing complex adaptation code.

### Features

* **Multi-Framework Support:** Auto-detects and bridges ESX, QBCore, and QBX.
* **Unified API:** Consistent function calls for Player, Economy, Inventory, and Vehicle management.
* **Auto-Detection:** Automatically configures itself based on started resources (Configurable to 'auto').
* **Extensive Integrations:** Built-in support for popular inventories, fuel scripts, garages, and target systems.
* **Developer Friendly:** Simplifies dependency management for paid or free releases.

### Requirements

* **FiveM Server** (Artifacts enabling Lua 5.4 recommended)
* **A Supported Framework:**
  * ESX Legacy
  * QBCore
  * QBX Core

### Installation

1. Download the `ak47_lib` resource.
2. Place the folder into your server's `resources` directory.
3. Add the following to your `server.cfg`:

```lua
ensure ak47_lib
```

### Configuration

The configuration is handled in `config.lua`. By default, most options are set to `'auto'`, meaning the lib will scan your server resources and select the appropriate integration automatically.

```lua
Config = {}

-- Framework: 'esx', 'qb', 'qbx', 'auto'
Config.Framework = 'auto'

-- Notification System: 'ox', 'esx', 'qb', 'qbx', 'custom'
Config.Notify = 'ox'

-- Progress Bar: 'ox', 'esx', 'qb', 'custom'
Config.Progressbar = 'ox'

-- Garage System: 'ak47_garage', 'cd_garage', 'qb-garages', etc.
Config.Garage = 'auto'

-- Vehicle Keys: 'ak47_vehiclekeys', 'wasabi_carlock', 'qs-vehiclekeys', etc.
Config.VehicleKey = 'auto'

-- Fuel System: 'LegacyFuel', 'ox_fuel', 'ps-fuel', etc.
Config.FuelScript = 'auto'

-- Inventory: 'ox_inventory', 'qs-inventory', 'qb-inventory', etc.
Config.Inventory = 'auto'

-- Banking: 'qb-banking', 'okokBanking', 'Renewed-Banking'
Config.Banking = 'auto'
```


# Framework

Here is the comprehensive documentation for the Unified Framework Functions of ak47\_lib. These functions act as the core compatibility layer, abstracting the differences between ESX, QBCore, and QB


# Client

The `ak47_lib` automatically detects the running framework. You can access these functions by importing the lib export in your script.

```lua
local Lib47 = exports['ak47_lib']:GetLibObject()
```

#### **`Lib47.Framework`**

A built-in variable that stores the active framework detected by the library. This is highly useful for writing dynamic, multi-framework scripts that need to execute different logic depending on the server's core environment.

Expected Values: `'esx'`, `'qb'`, or `'qbx'`.

```lua
--- @type string The name of the currently active framework
local currentFramework = Lib47.Framework

-- Example usage for framework-specific logic
if Lib47.Framework == 'qb' then
    print("Detected Framework: QBCore")
    -- Execute QBCore specific code here
    
elseif Lib47.Framework == 'esx' then
    print("Detected Framework: ESX")
    -- Execute ESX specific code here
    
elseif Lib47.Framework == 'qbx' then
    print("Detected Framework: Qbox")
    -- Execute Qbox specific code here
    
else
    print("No supported framework detected (Standalone)")
end
```

#### **`Lib47.GetCoreConfig`**

Retrieves the main configuration table of the active framework (e.g., `QBCore.Config` or `ESX.GetConfig()`). This is highly useful for fetching default framework settings without needing to directly reference the core objects, keeping your script standalone-friendly.

*Note: When using ESX, you can optionally pass a specific string `key` to return a single value instead of the entire table. In QBCore/Qbox, the `key` parameter is ignored and the full configuration table is always returned.*

```lua
--- @param key string|nil (Optional) Specific config key to fetch (Utilized by ESX)
--- @return table|any The core configuration table, or a specific value (if key is provided in ESX). Returns an empty table {} on failure.

-- Example 1: Fetching the entire config table (Works on all frameworks)
local coreConfig = Lib47.GetCoreConfig()

if coreConfig then
    print("Core configuration loaded successfully.")
    -- You can now access framework settings: e.g., coreConfig.DefaultSpawn
end

-- Example 2: Fetching a specific key (Useful for ESX servers)
if Lib47.Framework == 'esx' then
    local startingAccountMoney = Lib47.GetCoreConfig('StartingAccountMoney')
    print("Default starting money configured in ESX: " .. json.encode(startingAccountMoney))
end
```

### Client Player Data

These functions allow you to access player data directly on the client side, updated automatically when framework events fire.

#### `Lib47.GetJob`

Returns the local player's job data standardized.

```lua
--- @return table { name, label, payment, isboss, grade = { name, level } }
local job = Lib47.GetJob()

if job.name == 'police' then
    print("You are a cop!")
end
```

#### `Lib47.GetPlayerData`

Returns the raw framework-specific player data table (QBCore.PlayerData or ESX PlayerData).

```lua
--- @return table
local data = Lib47.GetPlayerData()
```

#### `Lib47.GetTargetMetaValue`

A utility function to fetch metadata from another player (server-side) via a callback. Useful for checking status like `isdead` or `inlaststand` on a target player.

```lua
--- @param targetServerId number
--- @param metaKey string
--- @return any
local isDead = Lib47.GetTargetMetaValue(targetPlayerId, 'isdead')
```

#### **`Lib47.GetCoreConfig`**

Retrieves the active framework's core configuration table.

```lua
--- @return table The core config table
local config = Lib47.GetCoreConfig()
```

#### **`Lib47.GetIdentifier`**

Returns the unique identifier (CitizenID/Identifier) for the local player directly from the client.

```lua
--- @return string The unique identifier
local identifier = Lib47.GetIdentifier()
```

#### **`Lib47.GetCharacterName`**

Returns the full character name of the local player.

```lua
--- @return string The full name (Firstname Lastname)
local name = Lib47.GetCharacterName()
```

#### **`Lib47.AddStress` / `Lib47.RemoveStress`**

Modifies the local player's stress levels across frameworks.

```lua
--- @param amount number
Lib47.AddStress(10)
Lib47.RemoveStress(10)
```

Here is the missing client-side documentation formatted in Markdown, ready to be added to your official API reference.


# Server

The `ak47_lib` automatically detects the running framework. You can access these functions by importing the lib export in your script.

```lua
local Lib47 = exports['ak47_lib']:GetLibObject()
```

#### **`Lib47.Framework`**

A built-in variable that stores the active framework detected by the library. This is highly useful for writing dynamic, multi-framework scripts that need to execute different logic depending on the server's core environment.

Expected Values: `'esx'`, `'qb'`, or `'qbx'`.

```lua
--- @type string The name of the currently active framework
local currentFramework = Lib47.Framework

-- Example usage for framework-specific logic
if Lib47.Framework == 'qb' then
    print("Detected Framework: QBCore")
    -- Execute QBCore specific code here
    
elseif Lib47.Framework == 'esx' then
    print("Detected Framework: ESX")
    -- Execute ESX specific code here
    
elseif Lib47.Framework == 'qbx' then
    print("Detected Framework: Qbox")
    -- Execute Qbox specific code here
    
else
    print("No supported framework detected (Standalone)")
end
```

#### **`Lib47.GetCoreConfig`**

Retrieves the main configuration table of the active framework (e.g., `QBCore.Config` or `ESX.GetConfig()`). This is highly useful for fetching default framework settings without needing to directly reference the core objects, keeping your script standalone-friendly.

*Note: When using ESX, you can optionally pass a specific string `key` to return a single value instead of the entire table. In QBCore/Qbox, the `key` parameter is ignored and the full configuration table is always returned.*

```lua
--- @param key string|nil (Optional) Specific config key to fetch (Utilized by ESX)
--- @return table|any The core configuration table, or a specific value (if key is provided in ESX). Returns an empty table {} on failure.

-- Example 1: Fetching the entire config table (Works on all frameworks)
local coreConfig = Lib47.GetCoreConfig()

if coreConfig then
    print("Core configuration loaded successfully.")
    -- You can now access framework settings: e.g., coreConfig.DefaultSpawn
end

-- Example 2: Fetching a specific key (Useful for ESX servers)
if Lib47.Framework == 'esx' then
    local startingAccountMoney = Lib47.GetCoreConfig('StartingAccountMoney')
    print("Default starting money configured in ESX: " .. json.encode(startingAccountMoney))
end
```

### Core Player & Identity

These functions handle identifying players and retrieving their basic information across all frameworks.

#### `Lib47.GetPlayer`

Retrieves the raw framework-specific player object (e.g., `xPlayer` for ESX, `Player` for QBCore).

```lua
--- @param source number The player's server ID
--- @return table|nil The player object
local player = Lib47.GetPlayer(source)
```

#### `Lib47.GetIdentifier`

Returns the unique identifier for the player (CitizenID for QBCore/QBX, Identifier for ESX).

```lua
--- @param source number The player's server ID
--- @return string The unique identifier
local identifier = Lib47.GetIdentifier(source)
```

#### **`Lib47.GetCharacterName`**

Returns the player's full character name directly from the server side. (Note: This function acts as the replacement for `Lib47.GetName`, which is marked for removal.)

```lua
--- @param source number The player's server ID
--- @return string The full name (Firstname Lastname)
local name = Lib47.GetCharacterName(source)
```

#### `Lib47.GetPhoneNumber`

Returns the player's phone number.

```lua
--- @param source number The player's server ID
--- @return string The phone number
local phone = Lib47.GetPhoneNumber(source)
```

#### `Lib47.GetLicense`

Returns the Rockstar license of the player.

```lua
--- @param source number The player's server ID
--- @return string The license
local license = Lib47.GetLicense(source)
```

#### `Lib47.GetSourceFromIdentifier`

Finds a player's server ID based on their identifier.

```lua
--- @param identifier string The unique identifier
--- @return number|nil The source ID (or nil if offline)
local source = Lib47.GetSourceFromIdentifier(identifier)
```

#### **`Lib47.GetCoreConfig`**

Retrieves the active framework's core configuration table on the server.

```lua
--- @param key string|nil (Optional for ESX)
--- @return table
local config = Lib47.GetCoreConfig()
```

#### **`Lib47.GetSource`**

Gets the server ID from a framework player object.

```lua
--- @param Player table The framework player object
--- @return number The source ID
local source = Lib47.GetSource(Player)
```

#### **`Lib47.GetPlayerFromIdentifier`**

Retrieves the framework player object using their identifier.

```lua
--- @param identifier string
--- @return table|nil The player object
local player = Lib47.GetPlayerFromIdentifier(identifier)
```

#### **`Lib47.GetIdentifierByType`**

Fetches a specific type of identifier (e.g., 'steam', 'license', 'discord') from a player.

```lua
--- @param source number The player's server ID
--- @param idtype string The type of identifier to find
--- @return string|nil The identifier string
local discord = Lib47.GetIdentifierByType(source, 'discord')
```

#### **`Lib47.GetNameFromIdentifier`**

Fetches a player's full character name directly from the database using their identifier.

```lua
--- @param identifier string
--- @return string The full name
local name = Lib47.GetNameFromIdentifier(identifier)
```

#### **`Lib47.GetMetaData` / `Lib47.SetMetaData`**

```lua
--- @param source number
--- @param key string
--- @return any
local value = Lib47.GetMetaData(source, 'hunger')

--- @param source number
--- @param key string
--- @param value any
Lib47.SetMetaData(source, 'hunger', 100)
```

#### **`Lib47.HasGroupPermission`**

Checks if a player belongs to a specific permission group.

```lua
--- @param source number
--- @param group string Group name (e.g., 'admin', 'mod')
--- @return boolean
local hasPerm = Lib47.HasGroupPermission(source, 'admin')
```

***

### Jobs, Gangs & Permissions

#### `Lib47.GetJob`

Returns a standardized job table.

```lua
--- @param source number
--- @return table { name, label, payment, isboss, grade = { name, level } }
local job = Lib47.GetJob(source)
print(job.name, job.grade.level)
```

#### `Lib47.SetJob`

Sets the player's job and grade.

```lua
--- @param source number
--- @param jobName string
--- @param grade number
Lib47.SetJob(source, 'police', 2)
```

#### `Lib47.GetGang`

Returns a standardized gang table.

```lua
--- @param source number
--- @return table { name, label, isboss, grade = { name, level } }
local gang = Lib47.GetGang(source)
```

#### `Lib47.SetGang`

Sets the player's gang and grade.

```lua
--- @param source number
--- @param gangName string
--- @param grade number
Lib47.SetGang(source, 'ballas', 1)
```

#### **`Lib47.HasPermission`**

Checks if a player meets any permission criteria (Ace, License, Identifier, or Group) defined in a configuration table. Optionally prints a success notification to the server console.

```lua
--- @param source number
--- @param Admin table { WithAce = boolean, WithLicense = table, WithIdentifier = table, WithGroup = table }
--- @param notify boolean|nil Prints resource and method to console if true
--- @return boolean
local AdminConfig = {
    WithAce = true,
    WithLicense = {
        ['license:yourlicensekeyhere123'] = true
    },
    WithIdentifier = {
        ['YourCitizenIDorESXIdentifier'] = true
    },
    WithGroup = {
        ['admin'] = true,
        ['superadmin'] = true
    }
}

local hasAccess = Lib47.HasPermission(source, AdminConfig, true)

if hasAccess then
    print("Player has required permissions")
end
```

#### `Lib47.IsAdmin`

Checks if the player has admin privileges (checks for 'command' ace permission).

```lua
--- @param source number
--- @return boolean
if Lib47.IsAdmin(source) then
    print("Player is admin")
end
```

***

### Economy

#### `Lib47.GetMoney`

Retrieves the balance of a specific account.

```lua
--- @param source number
--- @param account string 'money' (or 'cash'), 'bank', 'black_money'
--- @return number
local cash = Lib47.GetMoney(source, 'cash')
```

#### `Lib47.AddMoney`

Adds money to a specific account.

```lua
--- @param source number
--- @param account string
--- @param amount number
Lib47.AddMoney(source, 'bank', 5000)
```

#### `Lib47.RemoveMoney`

Removes money from a specific account.

```lua
--- @param source number
--- @param account string
--- @param amount number
Lib47.RemoveMoney(source, 'money', 100)
```

#### `Lib47.GetSocietyMoney`

Gets the money associated with a job/society. Supports `esx_addonaccount`, `qb-management`, `qb-banking`, `okokBanking`, and `Renewed-Banking`.

```lua
--- @param job string The society/job name
--- @param ignoreBankingExport boolean|nil (Optional) Skip external banking exports
--- @return number
local balance = Lib47.GetSocietyMoney('police', false)
```

#### `Lib47.AddSocietyMoney` / `Lib47.RemoveSocietyMoney`

Modifies society funds with optional reason tracking and external export bypassing.

```lua
--- @param job string The society/job name
--- @param money number The amount to add/remove
--- @param reason string|nil (Optional) The reason for the transaction
--- @param ignoreBankingExport boolean|nil (Optional) Skip external banking exports
Lib47.AddSocietyMoney('police', 1000, 'State Grant', false)
Lib47.RemoveSocietyMoney('police', 500, 'Vehicle Repair', false)
```

***

### Basic Vehicle Management

#### `Lib47.GetFrameworkVehicles`

Get all registered framework vehicles.

```lua
local vehicles = Lib47.GetFrameworkVehicles()
local vehicle = vehicles[GetHashKey('adder')]

if vehicle then
    print("Model:" .. vehicle.model, "Name: " .. vehicle.label, "Category: " .. vehicle.category, "Price:" .. vehicle.price)
end
```

#### `Lib47.GetFrameworkVehicleByHash`

Get a specific registered framework vehicle by hash.

```lua
--- @param hash string
local hash = GetHashKey('adder')
local vehicle = Lib47.GetFrameworkVehicleByHash(hash)

if vehicle then
    print("Model:" .. vehicle.model, "Name: " .. vehicle.label, "Category: " .. vehicle.category, "Price:" .. vehicle.
end
```

#### `Lib47.IsVehicleOwner`

Checks if a player owns a vehicle with a specific plate.

```lua
--- @param source number
--- @param plate string
--- @return boolean
local owned = Lib47.IsVehicleOwner(source, 'ABC 123')
```

#### `Lib47.GetVehicleOwner`

Returns the identifier of the owner of a specific plate.

```lua
--- @param plate string
--- @return string|nil Identifier
local owner = Lib47.GetVehicleOwner('ABC 123')
```

#### `Lib47.GeneratePlate`

Generates a unique plate based on a pattern.

```lua
--- @param format string (optional) Default: "AAAA 11A"
--- @param prefix string (optional)
--- @return string
local newPlate = Lib47.GeneratePlate("AA111111")
```

#### `Lib47.GiveVehicle`

Inserts a vehicle into the database (owned\_vehicles / player\_vehicles).

```lua
--- @param source number
--- @param model string|number Model name or hash
Lib47.GiveVehicle(source, 'adder')
```

### Offline Data Management

#### **`Lib47.GetAllOfflinePlayers`**

Fetches all players from the database with their basic character info, money, and job data.

```lua
--- @return table Array of player data tables
local players = Lib47.GetAllOfflinePlayers()
```

#### **`Lib47.GetJobs`**

Retrieves the shared list of all jobs and their grades from the framework.

```lua
--- @return table
local jobs = Lib47.GetJobs()
```

#### **`Lib47.GetOfflineMoney`**

Gets the balance of a specific account for an offline player from the database.

```lua
--- @param identifier string
--- @param account string 'money' (or 'cash'), 'bank'
--- @return number
local offlineBank = Lib47.GetOfflineMoney(identifier, 'bank')
```

#### **`Lib47.AddOfflineMoney` / `Lib47.RemoveOfflineMoney`**

Modifies the money balance of an offline player directly in the database.

```lua
--- @param identifier string
--- @param account string
--- @param amount number
Lib47.AddOfflineMoney(identifier, 'bank', 5000)
Lib47.RemoveOfflineMoney(identifier, 'cash', 500)
```

#### **`Lib47.GetOfflineMetaData` / `Lib47.SetOfflineMetaData`**

Gets or sets metadata for an offline player directly in the database.

```lua
--- @param identifier string
--- @param key string
--- @return any
local jailTime = Lib47.GetOfflineMetaData(identifier, 'injail')

--- @param identifier string
--- @param key string
--- @param value any
Lib47.SetOfflineMetaData(identifier, 'injail', 15)
```

### Inventory

#### **`Lib47.GetInventoryItems`**

Retrieves all items inside a specific inventory or player's inventory.

```lua
--- @param inventoryId string|number The inventory ID or player source
--- @return table List of items
local items = Lib47.GetInventoryItems(source)
```

#### **`Lib47.GetItemsByName`**

Retrieves all instances of a specific item from a player's inventory.

```lua
--- @param source number Player Server ID
--- @param item string Item name
--- @return table List of matched items
local matchedItems = Lib47.GetItemsByName(source, 'water')
```

#### **`Lib47.GetItemAmount`**

An alias/alternative to `Lib47.GetInventoryItem`. Gets the total count of a specific item in the player's inventory.

```lua
--- @param source number Player Server ID
--- @param item string Item name
--- @return number Total amount
local amount = Lib47.GetItemAmount(source, 'phone')
```


# Integration


# Client

This section covers the client-side functions located in `integration/client/`. These functions unify interactions with third-party resources like Target systems, Inventory (client actions), Fuel, Keys, and UI elements.

### Target System

The lib standardizes all target interactions using the `qb-target` data structure. If `ox_target` is used, the lib automatically converts the syntax (mapping `action` to `onSelect`, `job` to `groups`, etc.).

#### `Lib47.AddBoxZone`

Creates a box zone for targeting.

```lua
--- @param name string Unique name for the zone
--- @param center vector3 Central coordinates
--- @param length number Length of the box
--- @param width number Width of the box
--- @param options table Zone options (minZ, maxZ, debugPoly, heading, useZ)
--- @param targetoptions table List of options using qb-target structure
Lib47.AddBoxZone("my_zone", vector3(100.0, 200.0, 30.0), 2.0, 2.0, {
    minZ = 29.0,
    maxZ = 31.0,
    debugPoly = false,
    heading = 90.0
}, {
    options = {
        {
            type = "client", -- or "server", "command"
            event = "myscript:client:action",
            icon = "fas fa-user",
            label = "Interact",
            job = "police", -- qb-target standard
            gang = "ballas", -- qb-target standard
            item = "handcuffs", -- qb-target standard
            action = function() -- Optional function support if not using event
                print("Clicked")
            end
        }
    },
    distance = 2.5
})
```

#### `Lib47.AddPolyZone`

Creates a complex polygon zone.

```lua
--- @param name string
--- @param points table List of vector3 points
--- @param options table (minZ, maxZ, debugPoly)
--- @param targetoptions table
Lib47.AddPolyZone("my_poly", {
    vector3(100.0, 100.0, 30.0),
    vector3(105.0, 100.0, 30.0),
    vector3(105.0, 105.0, 30.0)
}, {
    minZ = 29.0,
    maxZ = 31.0
}, { --[[ targetoptions ]] })
```

#### `Lib47.AddCircleZone`

Creates a spherical/circular zone.

```lua
--- @param name string
--- @param center vector3
--- @param radius number
--- @param options table
--- @param targetoptions table
Lib47.AddCircleZone("my_circle", vector3(100.0, 200.0, 30.0), 1.5, {
    debugPoly = true
}, { --[[ targetoptions ]] })
```

#### `Lib47.AddTargetEntity`

Adds target options to specific entities (NetID or Local).

```lua
--- @param entities table|number Single entity or list of entities
--- @param options table qb-target standard options
Lib47.AddTargetEntity(entity, {
    options = {
        {
            icon = "fas fa-car",
            label = "Check Vehicle",
            action = function(entity)
                print("Checking " .. entity)
            end
        }
    },
    distance = 2.0
})
```

#### `Lib47.AddTargetModel`

Adds target options to specific models.

```lua
--- @param models string|number|table Model name/hash or list
--- @param options table
Lib47.AddTargetModel('prop_atm_01', {
    options = {
        {
            event = "bank:open",
            icon = "fas fa-money-bill",
            label = "Use ATM"
        }
    },
    distance = 1.5
})
```

#### `Lib47.RemoveZone`

Removes a registered zone by name.

```lua
Lib47.RemoveZone("my_zone")
```

***

### UI & Progress

The lib standardizes progress bars using the `ox_lib` data structure.

#### `Lib47.ShowProgress`

Displays a progress bar (Circle in Ox, standard bar in QB/ESX).

```lua
--- @param data table ox_lib progress structure
--- @param successCb function (optional)
--- @param cancelCb function (optional)
--- @return boolean true if completed, false if cancelled
local success = Lib47.ShowProgress({
    label = 'Repairing Vehicle...',
    duration = 5000,
    position = 'bottom',
    useWhileDead = false,
    canCancel = true,
    disable = {
        move = true,
        car = true,
        combat = true
    },
    anim = {
        dict = 'mini@repair',
        clip = 'fixing_a_ped',
        flag = 49
    },
    prop = {
        model = 'prop_tool_wrench',
        bone = 57005,
        pos = vec3(0.1, 0.0, 0.0),
        rot = vec3(0.0, 0.0, 0.0)
    }
}, function()
    print("Done!")
end, function()
    print("Cancelled!")
end)
```

#### `Lib47.Notify`

Sends a notification to the player.

```lua
--- @param msg string Message content
--- @param type string 'success', 'error', 'info'
--- @param duration number Duration in ms
Lib47.Notify("Vehicle Repaired", "success", 5000)
```

***

### Inventory (Client)

#### `Lib47.OpenStash`

Opens a stash inventory for the player.

```lua
--- @param identifier string Unique stash ID
--- @param name string Display label
--- @param weight number Max weight (in kg or grams depending on inv)
--- @param slots number Max slots
Lib47.OpenStash("shop_stash_1", "Shop Storage", 100000, 50)
```

#### `Lib47.OpenSearchInventory`

Opens another player's inventory (e.g., searching/robbing).

```lua
--- @param targetServerId number
Lib47.OpenSearchInventory(targetServerId)
```

#### `Lib47.CloseInventory`

Forces the inventory to close.

```lua
Lib47.CloseInventory()
```

#### `Lib47.SetInventoryBusy`

Sets the player's inventory state to busy (prevents opening).

```lua
--- @param state boolean
Lib47.SetInventoryBusy(true)
```

***

### Vehicles & Keys

#### `Lib47.GiveVehicleKey`

Gives keys for a specific vehicle to the player.

```lua
--- @param plate string
--- @param vehicle entity
--- @param virtual boolean (optional) For temporary keys if supported
Lib47.GiveVehicleKey("ABC 123", vehicleEntity)
```

#### `Lib47.RemoveVehicleKey`

Removes keys from the player.

```lua
--- @param plate string
--- @param vehicle entity
Lib47.RemoveVehicleKey("ABC 123", vehicleEntity)
```

#### `Lib47.SetVehicleFuel`

Sets the fuel level of a vehicle.

```lua
--- @param vehicle entity
--- @param amount number (0-100)
Lib47.SetVehicleFuel(vehicleEntity, 100.0)
```

#### `Lib47.StoreVehicleHousing`

Stores a vehicle in a housing garage (Supporting CD, OkOk, JG, Loaf, Ak47).

```lua
--- @param garageId string
--- @param vehicle entity
Lib47.StoreVehicleHousing("my_house_1", vehicleEntity)
```

#### `Lib47.OpenGarageHousing`

Opens the garage menu for a specific housing property.

```lua
--- @param garageId string
Lib47.OpenGarageHousing("my_house_1")
```

***

### Status

#### `Lib47.IsDead`

Checks if a player (or self) is dead. Supports Metadata checks and Animation checks.

```lua
--- @param target number|nil Target Server ID or nil for self
--- @return boolean
local dead = Lib47.IsDead() -- checks self
```

#### `Lib47.IsLastStand`

Checks if a player is in the "last stand" / downed state.

```lua
--- @param target number|nil
--- @return boolean
local downed = Lib47.IsLastStand()
```

#### `Lib47.IsIncapacitated`

Checks if the player is either dead OR in last stand.

```lua
--- @param target number|nil
--- @return boolean
if Lib47.IsIncapacitated() then
    print("Player is down or dead")
end
```


# Server

This section completes the API reference by covering Inventory, Server-Side Vehicle Keys, and Third-Party Banking integrations. These functions are located in `integration/server/` and exposed via the main `Lib47` object.

### Inventory

The lib provides a unified API for managing player inventories.

#### `Lib47.AddItem`

Adds an item to the player's inventory. Handles slot and metadata for supported inventories.

```lua
--- @param source number Player Server ID
--- @param item string Item name
--- @param amount number Count
--- @param slot number|nil (Optional) Specific slot
--- @param meta table|nil (Optional) Item metadata
--- @return boolean Success
local success = Lib47.AddItem(source, 'water', 5, nil, { quality = 100 })
```

#### `Lib47.RemoveItem`

Removes an item from the player's inventory.

```lua
--- @param source number Player Server ID
--- @param item string Item name
--- @param amount number Count
--- @return boolean Success
local success = Lib47.RemoveItem(source, 'water', 1)
```

#### `Lib47.GetInventoryItem`

Gets the count of a specific item in the player's inventory.

```lua
--- @param source number Player Server ID
--- @param item string Item name
--- @return number Count
local count = Lib47.GetInventoryItem(source, 'plastic')
```

#### `Lib47.HasEnoughItem`

Checks if the player has *at least* the specified amount of an item.

```lua
--- @param source number
--- @param item string
--- @param amount number
--- @return boolean
if Lib47.HasEnoughItem(source, 'money', 500) then
    print("Player can pay!")
end
```

#### `Lib47.CanCarryItem`

Checks if the player has enough weight/space to carry the item (Crucial for Ox/QB/QS).

```lua
--- @param source number
--- @param item string
--- @param amount number
--- @return boolean
if Lib47.CanCarryItem(source, 'stone', 10) then
    Lib47.AddItem(source, 'stone', 10)
else
    Lib47.Notify(source, "Inventory full!", "error")
end
```

#### `Lib47.GetItems`

Returns the server's master list of items (definitions).

```lua
--- @return table Key-value pair of items
local items = Lib47.GetItems()
print(items['water'].label)
```

#### `Lib47.GetItemLabel`

Returns the label of a specific item.

```lua
--- @param item string Item name
--- @return string Label
local label = Lib47.GetItemLabel('sandwich') -- Returns "Sandwich"
```

#### `Lib47.CreateUseableItem`

Registers a usable item callback.

```lua
--- @param item string Item name
--- @param cb function Callback function(source, item)
Lib47.CreateUseableItem('bandage', function(source, item)
    Lib47.RemoveItem(source, 'bandage', 1)
    Lib47.Notify(source, "Used Bandage", "success")
end)
```

***

### Vehicle Keys (Server)

These functions allow you to give or remove vehicle keys directly from the server side.

#### `Lib47.GiveVehicleKey`

Gives keys for a specific vehicle to a player.

```lua
--- @param source number Player Server ID
--- @param plate string Vehicle Plate
--- @param vehNetId number Network ID of the vehicle entity
--- @param virtual boolean (Optional) Give temporary/virtual keys
Lib47.GiveVehicleKey(source, "ABC 123", vehNetId, false)
```

#### `Lib47.RemoveVehicleKey`

Removes keys for a specific vehicle from a player.

```lua
--- @param source number Player Server ID
--- @param plate string Vehicle Plate
--- @param vehNetId number Network ID of the vehicle entity
--- @param virtual boolean (Optional) Remove temporary/virtual keys
Lib47.RemoveVehicleKey(source, "ABC 123", vehNetId, false)
```

***

### Banking Integration

it is important to note that the Integration layer (`integration/server/banking.lua`) extends these functions to support third-party banking resources automatically.

#### `Lib47.GetSocietyMoney`

Retrieves the current available balance of a specific job or society account.

```lua
--- @param job string
local balance = Lib47.GetSocietyMoney('police')
print(balance)
```

#### `Lib47.AddSocietyMoney`

Deposits funds into a specific job or society account.

```lua
--- @param job string
--- @param amount number
Lib47.AddSocietyMoney('police', 5000)
```

#### `Lib47.RemoveSocietyMoney`

Withdraws or removes funds from a specific job or society account.

```lua
--- @param job string
--- @param amount number
Lib47.RemoveSocietyMoney('police', 200)
```


# Interface


# Text UI

A lightweight, highly-optimized script for rendering 2D on-screen text and button prompts. Unlike the 3D Text UI, this version is entirely static and event-driven—meaning it has **zero background performance overhead**. It simply acts as a visual prompt and relies completely on your own external scripts to handle the logic, input polling, and distance checks.

**⚙️ Data Structures**

You can configure the 2D Text UI using either a simple string or a full configuration table for advanced control.

**Simple String (Shorthand)** If you just need to display simple text, you can pass a string directly. The system will automatically build the necessary table structure using your default configuration settings.

**The Main Configuration Table**

If you pass a table, this defines *where* the text is positioned on your screen and *what* it displays.

| **Property** | **Type** | **Default**                       | **Description**                                                       |
| ------------ | -------- | --------------------------------- | --------------------------------------------------------------------- |
| position     | `string` | `Config.Defaults.TextUI.position` | The CSS-based screen position (e.g., `'center-left'`, `'top-right'`). |
| options      | `table`  | Required                          | A list of text lines or button prompts (see below).                   |
| scale        | `float`  | `1.0`                             | Scaling factor for the UI element.                                    |

**The Options Table**

Because this script is purely visual, the `options` list only handles display parameters. It does **not** process `action` callbacks, `hold` timers, or `isVisible` checks. You must handle input processing in your own resource loops.

```lua
options = {
    { 
        label = "Access Laptop",    -- The text displayed on screen
        key = 38,                   -- (Optional) The FiveM Control Index (38 is E). Auto-fetches from Lib47.Keys
        keyName = "E",              -- (Optional) Manually override the visual key name displayed inside the box
    },
    { 
        label = "Exit Menu", 
        keyName = "ESC"             -- You can pass a keyName without a control index for pure visuals
    }
}
```

*Supported Positions:* `'top-left'`, `'top-right'`, `'top-center'`, `'bottom-left'`, `'bottom-right'`, `'bottom-center'`, `'center-left'`, `'center-right'`

***

**🛠️ API Reference**

Because 2D Text UI operates natively on the screen rather than in the 3D world, there is only **1 globally active 2D Text UI** allowed at a time per resource. You do not need to manage IDs; firing a new UI will instantly overwrite the previous one.

**1. ShowTextUi**

Renders the 2D Text UI on the screen. It stays visible until you manually hide it or overwrite it.

* Usage: `Lib47.ShowTextUi(data)` (where `data` is a `table` or a `string`)

**2. HideTextUi**

Instantly dismisses the active 2D Text UI.

* Usage: `Lib47.HideTextUi()`

***

**📝 Code Examples**

**Example A: Simple String (Quick Prompt)**

The fastest way to show a prompt. It automatically inherits the `position` from `Config.Defaults.TextUI.position`.

```lua
CreateThread(function()
    -- Show the prompt
    Lib47.ShowTextUi("Press [E] to talk to NPC")
    
    -- Wait for player to press E (You handle the input logic)
    while true do
        Wait(0)
        if IsControlJustReleased(0, 38) then
            print("Talked to NPC!")
            Lib47.HideTextUi() -- Hide it when done
            break
        end
    end
end)
```

**Example B: Advanced Layout**

Use this when you want to customize the position or show multiple keys simultaneously with clean UI button icons.

```lua
RegisterCommand('showui', function()
    Lib47.ShowTextUi({
        position = 'bottom-right',
        scale = 1.1,
        options = {
            { 
                label = "LOCK/UNLOCK", 
                key = 303, -- 'U' key
            },
            { 
                label = "ENGINE", 
                keyName = "G", -- Manual override
            }
        }
    })
end)

RegisterCommand('hideui', function()
    Lib47.HideTextUi()
end)
```

**Example C: Using Exports Directly**

If you do not use the `Lib47` global wrapper, you can use the resource exports directly via the `Interface` wrapper.

```lua
exports['ak47_lib']:ShowTextUi({
    position = 'top-center',
    options = {
        { label = "Entering Restricted Zone..." }
    }
})

-- Later
exports['ak47_lib']:HideTextUi()
```


# 3D Text UI

A lightweight, optimized standalone script for rendering 3D interactive text labels in the world. It features a dual-loop automatic culling system for high performance, proximity scaling, and support for "tap" and "hold" interactions with visual progress tracking.

#### ⚙️ Data Structures

Before using the functions, understand the data tables used to configure an interaction point.

**The Main Configuration Table**

This table defines *where* the text is and *how* it behaves.

| **Property** | **Type**  | **Default** | **Description**                                                             |
| ------------ | --------- | ----------- | --------------------------------------------------------------------------- |
| coords       | `vector3` | Required    | The world coordinates where the text should appear.                         |
| options      | `table`   | Required    | A list of interaction options (see below).                                  |
| distance     | `float`   | `2.5`       | The distance at which the UI switches to "Full" mode (interaction enabled). |
| maxDistance  | `float`   | `5.0`       | The max distance at which the text is visible at all.                       |
| scale        | `float`   | `1.0`       | Scaling factor for the UI element.                                          |
| arc          | `boolean` | `false`     | Visual flag passed to UI (optional style preference).                       |

**The Options Table**

The `options` list contains the specific inputs available to the player. Note: Callbacks are now defined directly inside each option via the `action` property.

```lua
options = {
    { 
        label = "PICK UP",          -- The action text displayed
        key = 38,                   -- The FiveM Control Index (38 is E)
        keyName = "E",              -- (Optional) The visual key name. Falls back to Lib47.Keys if omitted.
        action = function()         -- The function triggered upon interaction
            print("Item picked up!")
        end,
        isVisible = function()      -- (Optional) Determines if the option shows up at all
            return true 
        end,
        canInteract = function()    -- (Optional) If false, the option is visible but disabled/grayed out
            return true 
        end
    },
    { 
        label = "INSPECT", 
        key = 74,                   -- 74 is H
        keyName = "H",
        hold = 3,                   -- (Optional) Seconds button must be held to trigger
        action = function()
            print("Inspected!")
        end
    }
}
```

***

#### 🛠️ API Reference

This script exposes functions and assigns them to the global `Lib47` table. It utilizes an automatic slow culling loop (checking within 30.0 units) and a fast interaction loop, meaning you no longer need to check distances manually in your own threads.

**1. RegisterTextUi3d (Static Mode)**

Best for static, permanent locations (e.g., Shops, Duty points, ATMs). You register it once, and the script's internal culling handles the rest.

* Usage: `Lib47.RegisterTextUi3d(data)`
* Returns: `string` (The Interaction ID, derived from coordinates).

**2. ShowTextUi3d (Dynamic/Immediate Mode)**

Best for dynamic entities (like dropping an item on the ground). This registers the point temporarily. If you walk away, it will eventually be culled out unless updated.

* Usage: `Lib47.ShowTextUi3d(data)`
* Returns: `string` (Interaction ID).

**3. HideTextUi3d & RemoveTextUi3d**

Hides or completely removes an interaction point.

* Usage: `Lib47.HideTextUi3d(id)` or `Lib47.RemoveTextUi3d(id)`

***

#### 📝 Code Examples

**Example A: Static Location (Register Mode)**

Use this for permanent locations. Run this code once (e.g., at resource start). You do not need a loop.

```lua
CreateThread(function()
    local position = vector3(373.59, 328.58, 103.68)

    -- Register the point once, the script handles distance and culling automatically
    Lib47.RegisterTextUi3d({
        coords = position,
        distance = 2.5,
        maxDistance = 5.0,
        options = {
            { 
                label = "PICK UP", 
                key = 38,
                keyName = "E",
                action = function()
                    print("Player picked up item") 
                end
            },
            { 
                label = "INSPECT", 
                key = 74,
                keyName = "H",
                hold = 3, -- 3 Second Hold
                action = function()
                    print("Player inspected item") 
                end
            }
        },
    })
end)
```

**Example B: Dynamic State & Conditional Logic**

Use the `isVisible` and `canInteract` functions to dynamically change how the text behaves based on the player's state (e.g., job, money, or items).

```lua
CreateThread(function()
    local atmCoords = vector3(100.0, 100.0, 20.0)

    Lib47.RegisterTextUi3d({
        coords = atmCoords,
        distance = 2.0,
        options = {
            { 
                label = "USE ATM", 
                key = 38, 
                keyName = "E",
                isVisible = function()
                    -- Only show if the player is not in a vehicle
                    return not IsPedInAnyVehicle(PlayerPedId(), false)
                end,
                canInteract = function()
                    -- Visible, but grayed out if the player is dead
                    return not IsEntityDead(PlayerPedId())
                end,
                action = function()
                    print("Accessing ATM...")
                end
            }
        }
    })
end)
```

**Example C: Using Exports directly**

If you do not use the `Lib47` global wrapper, you can use the resource exports directly via the `Interface` wrapper.

```lua
exports['ak47_lib']:RegisterTextUi3d({
    coords = vector3(100.0, 100.0, 20.0),
    options = {
        { 
            label = "TEST", 
            key = 38,
            keyName = "E",
            action = function()
                print("Clicked")
            end
        }
    }
})
```


# Context-Menu

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2Fa1WZblrcWL9qpnXmYdhV%2Fimage.png?alt=media&amp;token=9df78f68-5518-40a0-97ca-32cee3bbc839" alt=""><figcaption></figcaption></figure>

### 🧭 Menu Types: Context vs. Menu

Before diving into the functions, it's important to understand the two distinct ways you can display data using `ak47_lib`:

1. Context Menus (`RegisterContext` & `ShowContext`): Best for static lists of actions where the user clicks an option to trigger an event, server event, or function. Usually mouse-driven.
2. Interactive Menus (`RegisterMenu` & `ShowMenu`): Best for complex interactions like checkboxes, side-scrolling lists, and real-time navigation feedback. Completely keyboard-driven (Arrow Keys, Enter, Backspace).

***

### 🛠️ Core Functions

#### `RegisterContext`

Registers a mouse-driven context menu. This data is cached and can be called anytime using `ShowContext`.

Syntax:

```lua
exports.ak47_lib:RegisterContext(data)
```

Example:

```lua
exports.ak47_lib:RegisterContext({
    id = 'player_actions',
    title = 'Player Actions', 
    position = 'top-right',
    canClose = true,
    options = {
        {
            title = 'Heal Player',
            description = 'Restores health to **100%**',
            icon = 'heart',
            iconColor = '#ff5555',
            onSelect = function() print("Heal button pressed!") end
        },
        {
            title = 'Give Weapon',
            description = 'Triggers a client event',
            icon = 'gun', 
            event = 'my_custom_client_event',
            args = { weapon = 'WEAPON_PISTOL', ammo = 250 }
        }
    }
})
```

#### `RegisterMenu`

Registers a keyboard-driven interactive menu. Features a master callback that triggers when the user presses `Enter` on an item.

Syntax:

```lua
exports.ak47_lib:RegisterMenu(data, callback(selected, scrollIndex, args))
```

Example:

```lua
exports.ak47_lib:RegisterMenu({
    id = 'settings_menu',
    title = 'Server Settings',
    position = 'top-left',
    onClose = function(keyPressed)
        print('Menu closed using: ' .. tostring(keyPressed))
    end,
    options = {
        { label = 'Godmode', icon = 'shield', checked = false, args = { setting = 'godmode' } },
        { label = 'Time of Day', icon = 'clock', values = {'Morning', 'Noon', 'Night'}, defaultIndex = 2 }
    }
}, function(selected, scrollIndex, args)
    print(('Selected Item: %s, Scroll Index: %s'):format(selected, tostring(scrollIndex)))
    if args then print('Args: ', json.encode(args)) end
end)
```

#### Display & Hide Functions

| **Function**  | **Description**                                                                 | **Example Usage**                                       |
| ------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `ShowContext` | Opens a registered context menu. You can specify if it should be keyboard only. | `exports.ak47_lib:ShowContext('player_actions', false)` |
| `ShowMenu`    | Opens a registered menu in strict keyboard-only mode.                           | `exports.ak47_lib:ShowMenu('settings_menu')`            |
| `HideContext` | Closes the active context menu.                                                 | `exports.ak47_lib:HideContext(true)`                    |
| `HideMenu`    | Closes the active interactive menu.                                             | `exports.ak47_lib:HideMenu(true)`                       |
| `GetOpenMenu` | Returns the string `id` of the currently open menu, or `nil`.                   | `local current = exports.ak47_lib:GetOpenMenu()`        |

#### `SetMenuOptions`

Dynamically updates the options of a registered menu without needing to re-register it.

Syntax:

```lua
-- Update ALL options
exports.ak47_lib:SetMenuOptions('menu_id', newOptionsTable)

-- Update a SINGLE option (e.g., updating the 2nd item)
exports.ak47_lib:SetMenuOptions('menu_id', updatedOptionData, 2)
```

***

### ⚙️ Configuration Properties

#### Menu Configuration (`data` table)

These properties are used at the root level of `RegisterContext` or `RegisterMenu`.

| **Property**   | **Type**   | **Description**                                                                                                                             |
| -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | `string`   | Required. A unique identifier for the menu.                                                                                                 |
| `title`        | `string`   | The display title at the top of the menu.                                                                                                   |
| `position`     | `string`   | `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right',` `center-left`, `center-right`                                               |
| `canClose`     | `boolean`  | If `true`, the user can press ESC/Backspace to close.                                                                                       |
| `disableInput` | `boolean`  | If `true`, disables all player controls while the menu is open.                                                                             |
| `options`      | `table`    | Required. An array of option objects (see below).                                                                                           |
| `onClose`      | `function` | Triggers when the menu is closed (receives the key pressed).                                                                                |
| `onSelected`   | `function` | (*RegisterMenu only*) Triggers when an item is hovered.                                                                                     |
| `onSideScroll` | `function` | (*RegisterMenu only*) Triggers when a side-scrolling list changes.                                                                          |
| `onCheck`      | `function` | (*RegisterMenu only*) Triggers when a checkbox is toggled.                                                                                  |
| `style`        | `table`    | <p>Custom react styling.<br><code>style = {</code><br>  <code>right = '5rem',</code><br>  <code>top = '10rem',</code><br><code>}</code></p> |

#### Option Configuration (Items inside `options`)

| **Property**      | **Type**  | **Description**                                                                              |
| ----------------- | --------- | -------------------------------------------------------------------------------------------- |
| `title` / `label` | `string`  | Required. The main text of the button. (`title` is preferred for Context, `label` for Menu). |
| `description`     | `string`  | Secondary text displayed as a tooltip or subtext.                                            |
| `icon`            | `string`  | FontAwesome icon name (e.g., `'car'`, `'shield'`) or an image URL/Path.                      |
| `iconColor`       | `string`  | Hex or RGB color for the icon (e.g., `'#ff5555'`).                                           |
| `iconAnimation`   | `string`  | FontAwesome animation class (e.g., `'spin'`, `'beat'`).                                      |
| `disabled`        | `boolean` | Grays out the button and prevents interaction.                                               |
| `readOnly`        | `boolean` | Prevents hovering/clicking, but doesn't look completely disabled.                            |
| `progress`        | `number`  | `0-100`. Displays a progress bar inside the button.                                          |
| `colorScheme`     | `string`  | Color used for the progress bar.                                                             |
| `args`            | `table`   | Custom data passed to events/callbacks when this item is interacted with.                    |
| `close`           | `boolean` | Set to `false` to keep the menu open after clicking. Defaults to `true`.                     |

#### Advanced Option Properties (Context Menus)

* `event` (`string`): Triggers a client event when clicked. Passes `args`.
* `serverEvent` (`string`): Triggers a server event when clicked. Passes `args`.
* `onSelect` (`function`): Anonymous function executed when clicked. Passes `args`.
* `menu` (`string`): The `id` of another menu to open when clicked (Submenu).

#### Advanced Option Properties (Interactive Menus)

* `checked` (`boolean`): Turns the option into a Checkbox.
* `values` (`table`): Array of strings or `{label, description}` objects. Turns the option into a Side-Scroller (Left/Right arrows).
* `defaultIndex` (`number`): The starting index (1-based) for a `values` list.

#### Metadata Panel (Player/Vehicle Stats)

You can attach a beautiful side-panel to any option by adding `image` and `metadata`.

```lua
{
    label = 'Inspect Vehicle',
    icon = 'magnifying-glass',
    image = 'https://docs.fivem.net/vehicles/t20.webp',
    metadata = {
        { label = 'Model', value = 'T20' },
        { label = 'Plate', value = 'LIB47DEV' },
        { label = 'Engine Health', value = '85%', progress = 85, colorScheme = '#fbc531' }
    }
}
```

***

### 📝 Usage Example

Here is a comprehensive example demonstrating submenus, side-scrolling, checkboxes, and metadata all working together seamlessly:

Context:

```lua
-- exports['ak47_lib']:RegisterContext({}) or
Lib47.RegisterContext({
    id = 'main_context_menu',
    title = 'Player Actions', 
    position = 'top-right',
    canClose = true,
    onExit = function()
        print("User closed the main menu with ESC")
    end,
    options = {
        {
            title = 'Heal Player',
            description = 'Restores health to **100%**',
            icon = 'heart',
            iconColor = '#ff5555',
            onSelect = function() print("Heal button pressed!") end
        },
        {
            title = 'Vehicle Management',
            description = 'Open vehicle options menu',
            icon = 'car',
            menu = 'sub_context_menu',
            arrow = true
        },
        {
            title = 'Downloading Data...',
            description = 'Extracting files from server',
            icon = 'spinner',
            iconAnimation = 'spin',
            progress = 65,
            colorScheme = '#00a8ff',
            readOnly = true 
        },
        {
            title = 'Admin Panel',
            description = 'You do not have permission',
            icon = 'lock',
            disabled = true 
        },
        {
            title = 'Give Weapon',
            description = 'Triggers a client event to give a weapon',
            icon = 'nui://ak47_qb_inventory/web/build/images/weapon_pistol.png', 
            event = 'my_custom_client_event',
            args = { weapon = 'WEAPON_PISTOL', ammo = 250 }
        },
        {
            title = 'Pay Parking Ticket',
            description = 'Deducts $500 via server event',
            icon = 'file-invoice-dollar',
            iconColor = '#4cd137',
            serverEvent = 'my_custom_server_event',
            args = { id = 101, amount = 500 }
        },
        {
            title = 'Inspect Vehicle Details',
            description = 'Hover to view vehicle statistics',
            icon = 'magnifying-glass',
            image = 'https://docs.fivem.net/vehicles/t20.webp',
            metadata = {
                { label = 'Model', value = 'T20' },
                { label = 'Plate', value = 'LIB47DEV' },
                { label = 'Engine Health', value = '85%', progress = 85, colorScheme = '#fbc531' },
                { label = 'Fuel Level', value = '20%', progress = 20, colorScheme = '#e84118' }
            },
            onSelect = function() print("Inspected vehicle!") end
        },
    }
})

-- exports['ak47_lib']:RegisterContext({}) or
Lib47.RegisterContext({
    id = 'sub_context_menu',
    title = 'Vehicle Options',
    position = 'top-right',
    menu = 'main_context_menu', 
    onBack = function() print("User clicked the back arrow to return to Player Actions") end,
    options = {
        {
            title = 'Toggle Engine',
            icon = 'power-off',
            iconColor = '#e1b12c',
            onSelect = function() print("Engine toggled!") end
        },
        {
            title = 'Lock Doors',
            icon = 'key',
            onSelect = function() print("Doors locked!") end
        }
    }
})

RegisterCommand('testmenu', function()
    -- Set keyboardOnly to true to test the arrow keys without mouse focus
    exports['ak47_lib']:ShowContext('main_context_menu')
    -- or
    Lib47.ShowContext('main_context_menu')
end)
```

Menu:

```lua
-- Register the comprehensive menu
-- exports['ak47_lib']:RegisterMenu({})
Lib47.RegisterMenu({
    id = 'comprehensive_test_menu',
    title = 'All Features Showcase',
    position = 'top-left',    -- 'top-left', 'top-right', 'bottom-left', 'bottom-right'
    disableInput = false,     -- Set to true to freeze player movement completely
    canClose = true,          -- Set to false to force the user to make a selection
    
    -- Triggers when the user presses ESC/Backspace to close the menu
    onClose = function(keyPressed)
        print(('Menu closed! Key pressed: %s'):format(keyPressed or 'N/A'))
    end,
    
    -- Triggers every time the user moves their selection up/down the list
    onSelected = function(selected, secondary, args)
        print(('Hovered Item: %s | Secondary Value: %s | Args: %s'):format(selected, tostring(secondary), json.encode(args)))
    end,
    
    -- Triggers instantly when the user presses Left/Right on a side-scroll option
    onSideScroll = function(selected, scrollIndex, args)
        print(('Scrolled Item: %s | New Scroll Index: %s'):format(selected, scrollIndex))
    end,
    
    -- Triggers instantly when the user presses Enter on a Checkbox option
    onCheck = function(selected, checked, args)
        print(('Toggled Checkbox: %s | Is Checked: %s'):format(selected, tostring(checked)))
    end,

    -- The actual menu items
    options = {
        -- 1. Standard Button
        {
            label = 'Basic Button',
            description = 'A standard button with a bottom tooltip description.'
        },
        
        -- 2. Button with Custom Icon Styling
        {
            label = 'Styled Icon',
            description = 'Using FontAwesome with custom color and spin animation.',
            icon = 'gear',
            iconColor = '#3498db',
            iconAnimation = 'spin'
        },
        
        -- 3. Checkbox Button
        {
            label = 'Toggle Godmode',
            description = 'Press Enter to toggle this checkbox.',
            icon = 'shield-halved',
            checked = false, 
            args = { setting = 'godmode' }
        },
        
        -- 4. Simple String Scroll List
        {
            label = 'Select Weapon',
            icon = 'gun',
            values = {'Pistol', 'SMG', 'Rifle', 'Shotgun'},
            defaultIndex = 3, -- Starts on 'Rifle' (Lua uses 1-based indexing, the code auto-converts it to JS 0-based!)
            args = { category = 'weapons' }
        },
        
        -- 5. Object Scroll List (Dynamic Descriptions per item)
        {
            label = 'Graphics Quality',
            icon = 'desktop',
            values = {
                { label = 'Low', description = 'Optimized for potato PCs.' },
                { label = 'Medium', description = 'Balanced performance and visuals.' },
                { label = 'Ultra', description = 'Maximum visual fidelity.' }
            },
            defaultIndex = 2
        },
        
        -- 6. Progress Bar Option (Read Only)
        {
            label = 'Server Load',
            description = 'Displays current server capacity.',
            icon = 'server',
            progress = 85,
            colorScheme = '#e74c3c', -- Red color for high load
            readOnly = true -- Prevents hovering/clicking
        },
        
        -- 7. Persistent Button (Does NOT close the menu)
        {
            label = 'Give $1000 (Keep Open)',
            description = 'Clicking this fires the callback but keeps the menu open.',
            icon = 'sack-dollar',
            iconColor = '#2ecc71',
            close = false, 
            args = { action = 'give_money' }
        },

        -- 8. Metadata Side Panel (Combines your old UI feature seamlessly)
        {
            label = 'View Player Stats',
            description = 'Hover to see the detailed side metadata panel.',
            icon = 'user',
            image = 'https://docs.fivem.net/vehicles/t20.webp', -- Image at top of side panel
            metadata = {
                { label = 'Name', value = 'John Doe' },
                { label = 'Job', value = 'LSPD' },
                { label = 'Hunger', value = '45%', progress = 45, colorScheme = '#f1c40f' },
                { label = 'Thirst', value = '90%', progress = 90, colorScheme = '#3498db' }
            }
        },
        
        -- 9. Disabled Button
        {
            label = 'Admin Actions',
            description = 'You do not have permission to use this.',
            icon = 'lock',
            disabled = true
        }
    }
}, function(selected, scrollIndex, args)
    -- Main Callback when a user PRESSES ENTER on a valid item
    print('--- MENU ITEM SELECTED ---')
    print('Selected Item Index:', selected)
    
    if scrollIndex then
        print('Scroll Index Chosen:', scrollIndex)
    end
    
    if args then
        print('Arguments:', json.encode(args))
        
        -- Example interaction
        if args.action == 'give_money' then
            print("Action triggered: Gave player money without closing the menu!")
        end
    end
end)

-- Command to open the menu
RegisterCommand('testnewmenu', function()
    -- ShowMenu implicitly sets keyboard-navigation to true, matching typical ox_lib behavior
    exports['ak47_lib']:ShowMenu('comprehensive_test_menu')
    -- or
    Lib47.ShowMenu('comprehensive_test_menu')
end)
```


# NPC Interaction

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FWyZ5hpymExoN8m6LEofN%2Fimage.png?alt=media&amp;token=d85e1293-be99-4cc3-828f-097ad35809fc" alt=""><figcaption></figcaption></figure>

The NPC Interaction module provides a highly immersive, cinematic dialogue system for your server. When a player interacts with an NPC, the script automatically aligns the player, creates a cinematic camera angle focusing on the NPC, and displays a sleek UI for dialogues and options. It fully supports nested submenus, conditional options, and event triggering.

***

### 📚 Available Exports

#### `RegisterNpcInteract`

Registers an interaction menu to a specific NPC entity. This must be called before attempting to show the menu.

Syntax:

```lua
exports['ak47_lib']:RegisterNpcInteract(entity, data)
```

Parameters:

| **Parameter** | **Type**  | **Description**                                                      |
| ------------- | --------- | -------------------------------------------------------------------- |
| `entity`      | `integer` | The network ID or handle of the ped/entity.                          |
| `data`        | `table`   | The configuration object containing dialogues, styling, and options. |

#### `ShowNpcInteract`

Opens a previously registered NPC interaction menu.

Syntax:

```lua
exports['ak47_lib']:ShowNpcInteract(id, focusIndex)
```

Parameters:

| **Parameter** | **Type**             | **Description**                                                 |
| ------------- | -------------------- | --------------------------------------------------------------- |
| `id`          | `string`             | The unique ID defined in your `RegisterNpcInteract` data table. |
| `focusIndex`  | `integer` (Optional) | The index of the option you want to be pre-selected/focused.    |

#### `HideNpcInteract`

Forcefully closes the currently open NPC interaction menu.

Syntax:

```lua
exports['ak47_lib']:HideNpcInteract(runOnExit)
```

Parameters:

| **Parameter** | **Type**  | **Description**                                                            |
| ------------- | --------- | -------------------------------------------------------------------------- |
| `runOnExit`   | `boolean` | If `true`, triggers the `onExit` callback defined in the NPC's data table. |

***

### ⚙️ Configuration Objects

#### The `data` Table

This table configures the overarching menu for the NPC.

| **Property** | **Type**   | **Description**                                                                       |
| ------------ | ---------- | ------------------------------------------------------------------------------------- |
| `id`         | `string`   | Required. A unique identifier for this interaction menu.                              |
| `colors`     | `table`    | Optional. Override default UI colors (`colorPrimary`, `colorSecondary`, `colorText`). |
| `dialogues`  | `table`    | An array of strings. The menu will pick one at random to display as a greeting.       |
| `menu`       | `string`   | Optional. The `id` of a parent menu. Enables native "Back" button functionality.      |
| `onExit`     | `function` | Optional. A callback triggered when the menu is closed.                               |
| `options`    | `table`    | Required. An array of `Option` objects.                                               |

#### The `Option` Object

Each item inside the `options` array represents a clickable button.

| **Property**  | **Type**   | **Description**                                                                |
| ------------- | ---------- | ------------------------------------------------------------------------------ |
| `label`       | `string`   | Required. The text displayed on the button.                                    |
| `icon`        | `string`   | FontAwesome icon name (e.g., `car`, `comments`, `person-walking-arrow-right`). |
| `iconColor`   | `string`   | Hex code to color the icon (e.g., `#3498db`).                                  |
| `isVisible`   | `function` | Returns a boolean. If `false`, the option is completely hidden.                |
| `disabled`    | `boolean`  | If `true`, the option is shown but grayed out/unclickable.                     |
| `canInteract` | `function` | Returns a boolean. Overrides `disabled` dynamically based on logic.            |
| `onSelect`    | `function` | Callback triggered when the player clicks this option.                         |
| `event`       | `string`   | A Client event to trigger when clicked.                                        |
| `serverEvent` | `string`   | A Server event to trigger when clicked.                                        |
| `menu`        | `string`   | Automatically opens another registered NPC menu `id` (acts as a submenu).      |

> Note on Navigation: You can navigate to a submenu using the `menu = 'target_id'` property inside an option, or manually inside the `onSelect` function by calling `exports['ak47_lib']:ShowNpcInteract('target_id')`.

***

### 💡 Examples

#### Example 1: Basic Shop Ped

A simple interaction with a single ped offering basic choices.

```lua
local shopPedId = 0 -- Assuming you have already spawned your ped here

exports['ak47_lib']:RegisterNpcInteract(shopPedId, {
    id = 'hardware_store_main',
    colors = {
        colorPrimary = "rgba(20, 20, 20, 0.95)", 
        colorSecondary = "#ffaa00",
        colorText = "#ffffff",
    },
    dialogues = {
        "Need some tools?",
        "Welcome to the hardware store. What can I get ya?",
        "Make it quick, I'm busy."
    },
    onExit = function()
        print("Player walked away from the hardware store.")
    end,
    options = {
        {
            label = 'Open Shop',
            icon = 'basket-shopping',
            iconColor = '#ffaa00',
            onSelect = function()
                -- Put your shop opening logic here
                TriggerEvent('inventory:client:openShop', 'hardware')
            end
        },
        {
            label = 'Leave',
            icon = 'person-walking-arrow-right',
            iconColor = '#e74c3c'
        }
    }
})

-- To open it (usually triggered via target system like qb-target or ox_target):
-- exports['ak47_lib']:ShowNpcInteract('hardware_store_main')
```

#### Example 2: Dialogue Trees (Submenus & Back Buttons)

This example demonstrates how to create a conversation tree where the player can ask questions, go into a submenu, and hit a "Back" button to return to the main choices.

```lua
local guidePedId = 0 -- Assuming spawned ped

-- 1. Register the Main Menu
exports['ak47_lib']:RegisterNpcInteract(guidePedId, {
    id = 'tour_guide_main',
    dialogues = {
        "Hello traveler! Want to know more about the city?"
    },
    options = {
        {
            label = 'Ask about the City',
            icon = 'city',
            iconColor = '#3498db',
            -- Use the `menu` parameter to automatically link a submenu!
            menu = 'tour_guide_city_info' 
        },
        {
            label = 'Goodbye',
            icon = 'hand-wave',
            iconColor = '#e74c3c'
        }
    }
})

-- 2. Register the Submenu
exports['ak47_lib']:RegisterNpcInteract(guidePedId, {
    id = 'tour_guide_city_info',
    menu = 'tour_guide_main', -- Defining this parent menu enables the native "Back" button
    dialogues = {
        "The city was founded in 1904. We have a rich history!"
    },
    options = {
        {
            label = 'Ask about the Mayor',
            icon = 'user-tie',
            iconColor = '#9b59b6',
            onSelect = function()
                print("The mayor is currently out of town.")
            end
        },
        {
            label = 'Ask about local gangs',
            icon = 'skull',
            iconColor = '#e67e22',
            onSelect = function()
                print("We don't talk about them around here...")
            end
        }
    }
})
```

#### Example 3: Conditional & Restricted Options

Sometimes you only want specific players (like police) to see an option, or you want to show an option but disable it if they don't have enough money.

```lua
local shadyPedId = 0

exports['ak47_lib']:RegisterNpcInteract(shadyPedId, {
    id = 'shady_dealer',
    dialogues = {
        "You got the cash or what?"
    },
    options = {
        {
            label = 'Buy Lockpick ($500)',
            icon = 'key',
            iconColor = '#f1c40f',
            -- Option is visible, but might be disabled
            canInteract = function()
                local hasEnoughMoney = true -- Replace with actual framework money check
                return hasEnoughMoney 
            end,
            onSelect = function()
                TriggerServerEvent('shadydealer:buyLockpick')
            end
        },
        {
            label = '(Police) Confiscate Goods',
            icon = 'shield-halved',
            iconColor = '#3498db',
            -- This completely hides the option if they aren't a cop
            isVisible = function()
                local isCop = false -- Replace with actual framework job check
                return isCop
            end,
            onSelect = function()
                TriggerServerEvent('police:confiscateNPC')
            end
        },
        {
            label = 'Walk away',
            icon = 'person-walking-arrow-right',
            iconColor = '#e74c3c'
        }
    }
})
```


# Progress

A flexible, lightweight, and animated progress bar system for FiveM. Supports 2D HUD bars, 3D in-world bars, animations, prop attachments, and control disabling.

### 🛠️ Core Functions

You can access the progress bar using exports.

#### **ShowProgress (Standard 2D)**

The "Fire and Forget" method. Handles the timer, animation, and cleanup automatically.

```lua
exports['ak47_lib']:ShowProgress(data, onFinish, onCancel)
```

| **Parameter** | **Type**   | **Description**                                                                |
| ------------- | ---------- | ------------------------------------------------------------------------------ |
| `data`        | `table`    | Configuration object (see below).                                              |
| `onFinish`    | `function` | (Optional) Callback when the bar reaches 100%.                                 |
| `onCancel`    | `function` | (Optional) Callback if the bar is cancelled (e.g., player moved or pressed X). |

#### **CancelProgress (Standard 2D)**

Cancel the running 2d progress.

```lua
exports['ak47_lib']:CancelProgress()
```

#### **CreateProgress (Advanced 3D)**

Returns a Progress Object that you must manually control. Useful for minigames, persistent HUD elements, or skills where progress isn't time-based.

```lua
local bar = exports['ak47_lib']:CreateProgress(data)
```

Object Methods:

* `bar.show()`: Displays the bar.
* `bar.update(value)`: Sets the percentage (0-100).
* `bar.destroy()`: Removes the bar immediately.

***

#### 📦 Data Object Configuration

The `data` table defines how the progress bar looks and behaves.

**Basic Settings**

| **Property** | **Type** | **Default** | **Description**                                |
| ------------ | -------- | ----------- | ---------------------------------------------- |
| `label`      | string   | "Progress"  | Text displayed on the bar.                     |
| `duration`   | number   | 3000        | Time in milliseconds (if not manual).          |
| `type`       | string   | "capsule"   | Visual style (see Styles below).               |
| `canCancel`  | boolean  | false       | If true, pressing `X` (73) cancels the action. |
| `reverse`    | boolean  | false       | If true, the bar goes from 100% to 0%.         |

**3D World Options**

| **Property** | **Type** | **Default** | **Description**                       |
| ------------ | -------- | ----------- | ------------------------------------- |
| `is3d`       | boolean  | false       | If true, the bar floats in the world. |
| `coords`     | vector3  | Player Pos  | World coordinates for the bar.        |
| `distance`   | number   | 10.0        | Max visibility distance for 3D bars.  |

**State Restrictions**

| **Property**    | **Type** | **Default** | **Description**                  |
| --------------- | -------- | ----------- | -------------------------------- |
| `useWhileDead`  | boolean  | false       | Cancel if player dies?           |
| `allowSwimming` | boolean  | false       | Cancel if player enters water?   |
| `allowFalling`  | boolean  | false       | Cancel if player falls?          |
| `allowRagdoll`  | boolean  | false       | Cancel if player gets ragdolled? |

**Control Disabling (`disable` sub-table)**

Prevent player input while the bar is active.

```lua
disable = {
    move = true,    -- Disables WASD / Jumping
    mouse = true,   -- Disables Camera Look
    combat = true,  -- Disables Shooting / Aiming
    car = true,     -- Disables Entering / Exiting Vehicles
    sprint = true   -- Disables Sprinting
}
```

**Animation (`anim` sub-table)**

Play an animation or scenario.

```lua
anim = {
    dict = "amb@world_human_gardener_plant@male@base", 
    clip = "base", 
    flag = 49,          -- Animation flag (default 49)
    blendIn = 3.0,      -- Speed to blend in
    scenario = "WORLD_HUMAN_WELDING" -- (Alternative to dict/clip)
}
```

**Props (`prop` sub-table)**

Attach one or more objects to the player.

```lua
prop = {
    model = "prop_tool_wrench",
    bone = 28422,       -- Bone Index (Right Hand)
    pos = vec3(0.0, 0.0, 0.0), -- Position Offset
    rot = vec3(0.0, 0.0, 0.0)  -- Rotation Offset
}
```

***

### 🎨 Visual Styles

You can change the appearance of the progress bar by setting the `type` field in the data object.

| **Type**        | **Description**                                                                                                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `capsule`       | <img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FNvvsty4COMx2QCKLAcBn%2Fimage.png?alt=media&amp;token=9c0d9600-aef7-46ef-af61-d9e779e4fbb3" alt="" data-size="original"> |
| `minimal`       | <img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FVuLbylwzT0whab0XvTUr%2Fimage.png?alt=media&amp;token=b390ef0c-a333-44b8-8d8d-cdc6b0d9f406" alt="" data-size="original"> |
| `segments`      | <img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FJgkbV3Mt4FQFT6pYTBEv%2Fimage.png?alt=media&amp;token=6d8f1e10-c723-4317-b294-f061bbaa3a8f" alt="" data-size="original"> |
| `pulse`         | ![](https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FEXxT3MDvIPdRBJYtay5f%2Fimage.png?alt=media\&token=4c41b93b-8d75-4c14-b310-f93af2e7f700)                                       |
| `radial-smooth` | ![](https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FNatglWLidDuz8VfaHG4O%2Fimage.png?alt=media\&token=fddceb70-4d23-4fe9-9a69-ec300b9d7de3)                                       |
| `radial-orbit`  | ![](https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FvyPb6ajg2Cj1HIKiJ9to%2Fimage.png?alt=media\&token=a80bf089-8b48-4f2c-a552-155e95cbfed6)                                       |
| `radial-ticks`  | ![](https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FfhgUNyOTEKxjU1pbK214%2Fimage.png?alt=media\&token=2efd9172-7d6e-41b8-b4c2-1353895aacdb)                                       |
| `radial-dashed` | ![](https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FIZD9Y3a37sg2APtc0bO5%2Fimage.png?alt=media\&token=e87fb3d1-4dd8-4241-a777-95efc3d6bcfa)                                       |

***

#### 📝 Examples

**Example 1: Basic Repair (Linear)**

Simple interaction with a callback.

```lua
exports['ak47_lib']:ShowProgress({
    duration = 5000,
    label = "Repairing Engine",
    type = "capsule",
    anim = {
        dict = "mini@repair",
        clip = "fixing_a_ped"
    }
}, function()
    print("Repair Complete!")
    -- Add repair logic here
end, function()
    print("Repair Cancelled")
end)
```

**Example 2: Medical Action (Complex)**

Uses props, animations, and disables controls.

```lua
exports['ak47_lib']:ShowProgress({
    duration = 7000,
    label = "Applying Bandage",
    type = "radial-smooth",
    canCancel = true,
    disable = {
        move = true,
        combat = true
    },
    anim = {
        dict = "missheistdockssetup1clipboard@idle_a",
        clip = "idle_a",
        flag = 49
    },
    prop = {
        model = "prop_paper_bag_small",
        bone = 28422,
        pos = vec3(0.1, 0.0, 0.0),
        rot = vec3(0.0, 0.0, 0.0)
    }
}, function()
    TriggerServerEvent('medical:heal')
end)
```

**Example 3: Manual HUD (Object Oriented)**

Useful for skill checks or minigames where the script controls the percentage.

```lua
-- 1. Create the bar object
local myBar = exports['ak47_lib']:CreateProgress({
    label = "Overheating",
    type = "segments",
    manual = true, -- IMPORTANT: Disables auto-timer
    initial = 0
})

-- 2. Show it
myBar.show()

-- 3. Update it in your own loop
CreateThread(function()
    local heat = 0
    while heat < 100 do
        heat = heat + 1
        myBar.update(heat) -- Update UI
        Wait(100)
    end
    
    -- 4. Destroy it when done
    myBar.destroy()
end)
```

**Example 4: 3D World Progress**

Attach a progress bar to a specific coordinate in the world (e.g., a door being drilled).

```lua
local doorCoords = vector3(120.5, -150.2, 30.0)

exports['ak47_lib']:ShowProgress({
    duration = 10000,
    label = "Drilling Lock",
    type = "radial-orbit",
    is3d = true,        -- Enable 3D mode
    coords = doorCoords, -- Where to show the bar
    distance = 15.0,    -- Visible distance
    anim = {
        scenario = "WORLD_HUMAN_WELDING"
    }
}, function()
    print("Door Opened")
end)
```


# Minigame

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2Fdnsij4IB8owj6XozngtI%2F5.png?alt=media&amp;token=6b6eb6b2-cff1-4fa2-a93c-c99ee4129a20" alt=""><figcaption></figcaption></figure>

The `ak47_lib` provides a versatile, highly configurable "tension" style minigame. It is perfect for activities like fishing, lockpicking, hacking, or repairing. The minigame yields the current thread using promises, meaning it pauses your script's execution until the player either wins or loses, keeping your code clean and synchronous-looking.

### 🛠️ Methods

#### `StartTensionMinigame`

Triggers the minigame UI and waits for the player's result.

```lua
-- Using Exports
local success = exports.ak47_lib:StartTensionMinigame(variant, difficulty, customSettings)

-- Using the Global Object (if initialized in your script)
local success = Lib47.StartTensionMinigame(variant, difficulty, customSettings)
```

Parameters:

* `variant` (string): The playstyle of the minigame.
  * *Options:* `'classic'`, `'momentum'`, `'shrinking'`, `'frenzy'`
  * *Default:* `'classic'`
* `difficulty` (string): The preset difficulty level.
  * *Options:* `'easy'`, `'medium'`, `'hard'`, `'expert'`, `'impossible'`
  * *Default:* `'medium'`
* `customSettings` (table, *optional*): A table of specific parameters to override the default config for this specific run (e.g., custom icons, bar sizes, speeds).

Returns:

* `success` (boolean): Returns `true` if the player successfully completes the minigame, and `false` if they fail or cancel it.

***

#### `CancelMinigame`

Forcefully closes the minigame and resolves the pending promise as `false`.

```lua
exports.ak47_lib:CancelMinigame()

-- Or via Global
Lib47.CancelMinigame()
```

***

### ⚙️ Variants & Configuration

The minigame relies on predefined settings found in `Config.Defaults.Minigame.tension`. You can select different variants depending on the context of the action:

1. Classic: A standard tension bar where the player must keep the target inside the zone.
2. Momentum: Introduces physics (thrust, gravity, friction). Great for mechanical tasks like lockpicking or hotwiring.
3. Shrinking: The safe zone dynamically changes size. Excellent for hacking or precision tasks.
4. Frenzy: The target speed randomly spikes. Perfect for catching aggressive fish or subduing a target.

**Available Override Settings (`customSettings`)**

You can pass any of these keys into the `customSettings` table to dynamically alter the minigame without changing `config.lua`:

* `icon`: FontAwesome class string (e.g., `'fa-solid fa-key'`).
* `barSize`: Width/size of the capture zone.
* `fishSpeed` / `normSpeed` / `frenzySpeed`: Movement speed of the target.
* `jumpChance` / `jumpNorm` / `jumpFrenzy`: Probability of the target sporadically jumping.
* `gain` / `loss`: Rate at which the progress bar fills or depletes.
* `thrust` / `gravity` / `friction`: (Momentum variant only) Physics modifiers.
* `startSize` / `minSize`: (Shrinking variant only) Size constraints.

***

### 💻 Usage Examples

#### Example 1: Basic Fishing

Using the `frenzy` variant on `medium` difficulty. This uses the default config settings and standard icon.

```lua
RegisterNetEvent('my_fishing:catchFish', function()
    -- Start the minigame and wait for result
    local success = exports.ak47_lib:StartTensionMinigame('frenzy', 'medium')
    
    if success then
        print("You successfully caught the fish!")
        -- Give item logic here
    else
        print("The fish got away...")
    end
end)
```

#### Example 2: Lockpicking with Custom Icon

Using the `momentum` variant on `easy` difficulty. We will override the default icon to look like a key, and manually adjust the bar size to make it slightly harder.

```lua
RegisterNetEvent('my_robbery:lockpickDoor', function()
    local customOverrides = {
        icon = 'fa-solid fa-key',  -- Changes the UI icon to a key
        barSize = 15               -- Overrides the default barSize for 'easy' (which is normally 35)
    }
    
    local success = exports.ak47_lib:StartTensionMinigame('momentum', 'easy', customOverrides)
    
    if success then
        print("Door successfully unlocked!")
        -- Unlock logic here
    else
        print("Your lockpick broke!")
        -- Break item logic here
    end
end)
```

#### Example 3: Hacking a Terminal

Using the `shrinking` variant on `hard` difficulty with an adjusted gain rate to make the hack take longer.

```lua
RegisterNetEvent('my_heist:hackTerminal', function()
    local hackerSettings = {
        icon = 'fa-solid fa-terminal',
        gain = 0.15, -- Slower progress gain (hard preset is 0.35)
        loss = 0.30  -- Faster progress loss (hard preset is 0.15)
    }
    
    local success = exports.ak47_lib:StartTensionMinigame('shrinking', 'hard', hackerSettings)
    
    if success then
        print("Firewall bypassed. Downloading data...")
    else
        print("Access Denied. Alarm triggered!")
    end
end)
```

#### Example 4: Canceling the Minigame Externally

If the player takes damage or gets arrested while doing the minigame, you can forcefully cancel it.

```lua
-- Assume the minigame is currently active from another thread
AddEventHandler('gameEventTriggered', function(eventName, data)
    if eventName == "CEventNetworkEntityDamage" then
        local victim = data[1]
        if victim == PlayerPedId() then
            -- Player took damage, cancel the UI immediately
            exports.ak47_lib:CancelMinigame()
            print("Minigame interrupted due to taking damage!")
        end
    end
end)
```


# SoundPlayer

The Sound Manager API is a highly structured, high-performance engine allowing for the creation, manipulation, and synchronization of 2D and 3D audio. It features spatialization, occlusion, global networking, automatic entity tracking, server-side time synchronization for late-joiners, full 5-band graphic equalizer support, and automatic resource sweeping to prevent audio leaks.

### **🎵 Accessing the API**

You can access the sound system from any other resource using the OOP wrapper or functional exports:

```lua
-- Option A: OOP Class Instance (Recommended)
local sound = exports['ak47_lib']:CreateSound(data)

-- Option B: Internal Lib Access
local sound = Lib47.CreateSound(data)
```

### **🛠 Constructor**

`CreateSound(data)`

Creates a new sound instance. This is the main entry point for the API.

Parameters (`data` table)

| **Property**     | **Type** | **Default**                             | **Description**                                                                                        |
| ---------------- | -------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `url`            | String   | Required                                | The direct URL or file path to the audio file (mp3, ogg, wav).                                         |
| `coords`         | vector3  | `nil`                                   | The coordinates for 3D audio. If omitted, the sound defaults to 2D (global UI sound).                  |
| `soundId`        | String   | Generated                               | A unique identifier. Provide one if you need to reference it strictly across network events.           |
| `is3d`           | Boolean  | `true` (if coords)                      | Forces the sound to be 3D or 2D.                                                                       |
| `volume`         | Number   | `0.5`                                   | The initial volume of the sound (0.0 to 1.0).                                                          |
| `maxDistance`    | Number   | `20.0`                                  | The distance at which the sound ceases to be heard (for 3D sounds).                                    |
| `rate`           | Number   | `1.0`                                   | The playback speed. `1.0` is normal, `0.5` is half speed, `2.0` is double speed.                       |
| `loop`           | Boolean  | `false`                                 | If `true`, the audio will restart automatically when it finishes.                                      |
| `interiorEffect` | Boolean  | `false`                                 | If `true`, the sound is occluded (muffled) if the player is in a different interior than the source.   |
| `global`         | Boolean  | `false`                                 | If `true`, playback, pausing, and seeking are synced to all clients (and late joiners) via the server. |
| `eq`             | Table    | `{sub=0, bass=0, mid=0, high=0, air=0}` | A 5-band graphic equalizer mix (values in decibels from -24 to +12).                                   |

Returns

Returns a Sound Object containing chainable methods to control the audio.

### **🧬 Sound Object Methods**

Once you have created a sound object, use the following methods to control it dynamically. *Most methods return the Sound Object, allowing for method chaining.*

Playback Controls

* `:play()` - Starts or resumes the audio playback.
* `:pause()` - Pauses the audio playback.
* `:destroy()` - Stops the sound and cleans up NUI resources. (Assign to variable: `mySound = mySound:destroy()`).
* `:seek(seconds)` - *\[NEW]* Jumps the audio to a specific timestamp. If `global = true`, this syncs the exact timestamp across all players via the server.

Parameter Updates

* `:setVolume(volume)` - Updates the volume dynamically (Local).
* `:setVolumeGlobal(volume)` - *\[NEW]* Updates the volume dynamically and syncs the change to all players on the server.
* `:setRate(rate)` - Updates the playback speed dynamically.
* `:setMaxDistance(distance)` - Updates the max hearing distance for 3D sounds.
* `:setEqualizer(eqTable)` - *\[NEW]* Updates the 5-band graphic equalizer dynamically (Local).
* `:setEqualizerGlobal(eqTable)` - *\[NEW]* Updates the equalizer dynamically and syncs the exact audio mix to all players on the server.

Spatial & Tracking Controls

* `:updateCoords(coords)` - Updates the static location of the sound source.
* `:attachToEntity(netId, offset)` - *\[NEW]* Automatically tracks and attaches the sound to a moving network entity (e.g., a vehicle). The API handles the coordinate loop internally.
* `:attachToPlayer(serverId, offset)` - *\[NEW]* Automatically tracks and attaches the sound to a specific player's ped.
* `:detach()` - *\[NEW]* Stops tracking an entity/player and freezes the sound at its current world coordinates.

Utilities

* `:getInfo()` - Asynchronously retrieves the current state of the audio `{ duration, currentTime }`. Yields via `Citizen.Await`.

### **🧰 Functional Exports (Non-OOP)**

If your external script does not wish to retain the Sound Object, you can manage active sounds using direct exports utilizing the `soundId`:

```lua
exports['ak47_lib']:AttachToEntity(soundId, netId, vector3(0, 0, 0))
exports['ak47_lib']:Seek(soundId, timeInSeconds)
exports['ak47_lib']:SetVolumeGlobal(soundId, volume)
exports['ak47_lib']:SetEqualizer(soundId, { sub = 6, bass = 2, mid = 0, high = 0, air = 0 })
exports['ak47_lib']:SetEqualizerGlobal(soundId, { sub = -12, bass = -12, mid = 5, high = 5, air = 10 })
exports['ak47_lib']:Destroy(soundId)
```

**🧹 Automatic Resource Sweeping**

Note to Developers: You do not need to manually destroy sounds when your script restarts. The Sound Manager API actively listens for `onResourceStop`. If your script crashes or is stopped, the API will instantly sweep and destroy all sounds created by your specific resource, preventing ghost audio loops.

### **📚 Examples**

#### Example 1: Looping Background Ambience

Plays a 2D sound that repeats forever. Perfect for weather effects or UI music.

```lua
local rain = exports['ak47_lib']:CreateSound({
    url = "sounds/rain_loop.ogg",
    volume = 0.2,
    is3d = false,
    loop = true
}):play()
```

#### Example 2: Global Siren (Syncs to all players)

A loud alarm that loops and is heard by everyone on the server. Late-joiners will automatically hear this if they enter the area.

```lua
local alarm = exports['ak47_lib']:CreateSound({
    soundId = "prison_alarm",
    url = "https://mysounds.com/alarm.mp3",
    coords = vector3(1800.0, 2600.0, 45.0),
    maxDistance = 300.0,
    volume = 1.0,
    global = true, 
    loop = true    
}):play()

-- Stop it after 30 seconds
SetTimeout(30000, function()
    if alarm then alarm = alarm:destroy() end
end)
```

#### Example 3: Moving Entity Sound (Automated Tracking)

*Updated: You no longer need a manual `Citizen.CreateThread` to update coordinates.* The API handles high-frequency coordinate tracking internally

```lua
local vehicleNetId = VehToNet(myVehicle)

local carRadio = exports['ak47_lib']:CreateSound({
    url = "sounds/music_track.wav",
    maxDistance = 40.0,
    volume = 0.8
})

-- Attach to the vehicle, offset slightly to the trunk, and play
carRadio:attachToEntity(vehicleNetId, vector3(0.0, -2.0, 0.5)):play()
```

#### Example 4: Synchronized Music Player (Seeking)

A perfect setup for a DJ Booth or boombox. Plays a song, seeks to the drop, and syncs the exact timestamp to all players.

```lua
local djBooth = exports['ak47_lib']:CreateSound({
    soundId = "club_dj_booth",
    url = "https://mysounds.com/club_mix.mp3",
    coords = vector3(120.5, -30.0, 15.0),
    maxDistance = 50.0,
    global = true, -- Enables server timestamping
})

djBooth:play()

-- Skip to 1 minute and 30 seconds into the song
-- This will sync instantly for all current players AND any player who logs in 10 minutes later.
djBooth:seek(90.0) 

-- Later on, lower the volume for everyone
djBooth:setVolumeGlobal(0.2)
```

#### Example 5: Audio Shaping with 5-Band Equalizer (Muffled/Bass-Boost)

Applies a custom mix to a sound upon creation, and dynamically alters the mix later. Perfect for simulating muffled music outside a nightclub, or adding bass to a vehicle boombox.

```lua
local clubMusic = exports['ak47_lib']:CreateSound({
    soundId = "nightclub_main",
    url = "https://mysounds.com/club_mix.mp3",
    coords = vector3(120.5, -30.0, 15.0),
    maxDistance = 50.0,
    global = true,
    -- Initial mix: Heavy Sub/Bass, normal mids/highs
    eq = { sub = 8, bass = 6, mid = 0, high = 0, air = 2 } 
})

clubMusic:play()

-- Example: Player walks outside the club door. 
-- Dynamically change the global mix to sound muffled (cut all treble, boost low-mids)
clubMusic:setEqualizerGlobal({ 
    sub = 2, 
    bass = 4, 
    mid = -5, 
    high = -15, 
    air = -24 
})
```


# Checklist

The Checklist API allows you to display a persistent, interactive task list on the player's HUD. It supports main tasks, nested sub-tasks, progress states, and rich text formatting (keys/mouse icons).

### 🛠️ Exports

#### `ShowChecklist`

Displays the checklist UI with a list of tasks.

<pre class="language-lua"><code class="lang-lua">exports['ak47_lib']:ShowChecklist(tasks, title, position)
-- or 
<strong>Lib47.ShowChecklist(tasks, title, position)
</strong></code></pre>

Parameters:

| **Argument** | **Type** | **Optional** | **Description**                                                                                                      |
| ------------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------- |
| `tasks`      | Table    | No           | A table containing the task objects (see [Task Structure](https://www.google.com/search?q=%23task-structure) below). |
| `title`      | String   | Yes          | The header title of the checklist. Defaults to Config value.                                                         |
| `position`   | String   | Yes          | Screen position. Options: `'top'`, `'center'`, `'bottom'`.                                                           |

***

#### `UpdateChecklist`

Updates the completion status of a specific task or sub-task.

```lua
exports['ak47_lib']:UpdateChecklist(index, isComplete, subIndex)
-- or
Lib47.UpdateChecklist(index, isComplete, subIndex)
```

Parameters:

| **Argument** | **Type** | **Optional** | **Description**                                              |
| ------------ | -------- | ------------ | ------------------------------------------------------------ |
| `index`      | Number   | No           | The 1-based index of the main task in the list.              |
| `isComplete` | Boolean  | No           | `true` to mark as done, `false` to uncheck.                  |
| `subIndex`   | Number   | Yes          | The 1-based index of a sub-task (if updating a nested item). |

***

#### `HideChecklist`

Removes the checklist from the screen.

```lua
exports['ak47_lib']:HideChecklist()
-- or
Lib47.HideChecklist()
```

***

### 🧬 Data Structures

#### Task Structure

When sending the `tasks` table to `ShowChecklist`, each item should follow this format:

```lua
{
    label = "Task Name",      -- (String) Text to display (supports Rich Text)
    completed = false,        -- (Boolean) Initial state
    subTasks = {              -- (Table, Optional) Nested tasks
        { label = "Sub A", completed = false },
        { label = "Sub B", completed = false }
    }
}
```

***

### 🎨 Rich Text Formatting

You can inject keyboard keys and mouse icons directly into your task labels using the following tags:

| **Tag**      | **Result**       | **Description**                                   |
| ------------ | ---------------- | ------------------------------------------------- |
| `<k>KEY</k>` | Keyboard E       | Renders a styled keyboard key (e.g., `<k>E</k>`). |
| `<m></m>`    | Mouse            | Renders a Mouse icon                              |
| `<m>1</m>`   | 🖱️ Mouse Left   | Renders a Left Mouse Button icon.                 |
| `<m>2</m>`   | 🖱️ Mouse Right  | Renders a Right Mouse Button icon.                |
| `<m>3</m>`   | 🖱️ Mouse Middle | Renders a Middle Mouse/Scroll icon.               |

***

### 💡 Examples

#### Example 1: Simple Task List

A basic list positioned at the top left of the screen.

```lua
local tasks = {
    { label = "Go to the police station", completed = false },
    { label = "Talk to the Captain", completed = false },
    { label = "Get your badge", completed = false }
}

-- Display the checklist
exports['ak47_lib']:ShowChecklist(tasks, "New Recruit", "top")
```

#### Example 2: Complex Mission (Sub-tasks & Rich Text)

A mission list centered on the screen featuring interaction keys and nested objectives.

```lua
local missionTasks = {
    { label = "Breach the front door <k>E</k>", completed = false },
    { 
        label = "Secure the Evidence", 
        completed = false,
        subTasks = {
            { label = "Hack the laptop <m>1</m>", completed = false },
            { label = "Steal the hard drive", completed = false }
        }
    },
    { label = "Escape via Roof", completed = false }
}

-- Display centered
exports['ak47_lib']:ShowChecklist(missionTasks, "Heist Setup", "center")
```

#### Example 3: Updating Progress

How to update the mission created in Example 2 dynamically.

```lua
CreateThread(function()
    -- 1. Complete "Breach the front door" (Index 1)
    Wait(5000)
    exports['ak47_lib']:UpdateChecklist(1, true)

    -- 2. Complete "Hack the laptop" (Index 2, Sub-index 1)
    Wait(5000)
    exports['ak47_lib']:UpdateChecklist(2, true, 1)

    -- 3. Complete "Steal the hard drive" (Index 2, Sub-index 2)
    Wait(2000)
    exports['ak47_lib']:UpdateChecklist(2, true, 2)
    
    -- 4. Complete the main "Secure the Evidence" task visually (Index 2)
    Wait(1000)
    exports['ak47_lib']:UpdateChecklist(2, true)

    -- 5. Finish Mission and Hide
    Wait(5000)
    exports['ak47_lib']:HideChecklist()
end)
```

#### Example 4:&#x20;

```lua
local masterTasks = {
    { label = "Define Shop Zone", completed = false },
    { label = "Place Sign", completed = false },
    { label = "Buying/Selling Point", completed = false },
    { label = "Shopkeeper Position", completed = false },
    { 
        label = "Setup Baskets", 
        completed = false,
        subTasks = {
            { label = "Set Position", completed = false },
            { label = "Navigation Mesh", completed = false },
            { label = "Queue Lines", completed = false },
        }
    },
    { label = "Billing Action", completed = false },
    { label = "Shop Actions", completed = false },
    { label = "Boss Menu", completed = false },
    { label = "Garage Spawn", completed = false },
    { label = "Setup Doors", completed = false },
}

Lib47.ShowChecklist(masterTasks, "Creation Checklist")
```

<div align="left"><figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FFJMRcaqN6qfFP5gGV4Rw%2Fimage.png?alt=media&amp;token=cdb22704-1e34-45e7-a98d-63b9ed12fbec" alt=""><figcaption></figcaption></figure></div>

***

### ℹ️ Additional Info

> Night Mode: The UI automatically adjusts its background opacity between 21:00 and 06:00 in-game time (managed via `GetClockHours`).

> Lib47: If you are using the internal `Lib47` files, the functions are also available via `Lib47.ShowChecklist`, `Lib47.UpdateChecklist`, and `Lib47.HideChecklist`.


# Objective

The Objective API is a flexible HUD component designed for the right side of the screen. It can display everything from a single line of text to complex instruction sets with multiple sections, lists, and rich text formatting.

### 🛠️ Exports

#### `ShowObjective`

Displays the objective card with specified content and positioning.

```lua
exports['ak47_lib']:ShowObjective(text, title, position)
--
Lib47.ShowObjective(text, title, position)
```

Parameters:

| **Argument** | **Type**     | **Optional** | **Description**                                                                       |
| ------------ | ------------ | ------------ | ------------------------------------------------------------------------------------- |
| `text`       | String/Table | No           | The content to display. Can be a string, a simple list, or a complex sectioned table. |
| `title`      | String       | Yes          | The header title. Defaults to Config value.                                           |
| `position`   | String       | Yes          | Screen position: `'top'`, `'center'`, or `'bottom'`.                                  |

***

#### `HideObjective`

Removes the objective card from the HUD.

```lua
exports['ak47_lib']:HideObjective()
-- or
Lib47.HideObjective()
```

***

### 🧬 Data Structures

The `text` parameter is highly versatile and handles four main types of input:

#### 1. Simple String

A single line of descriptive text.

```lua
local text = "Locate the package in the warehouse."
```

#### 2. Simple List

An array of strings rendered as a bulleted list.

```lua
local text = {
    "Collect the evidence",
    "Escape the police"
}
```

#### 3. Named Section

An object containing a sub-header and a list.

```lua
local text = {
    Title = "Quick Menu",
    List = {
        "Option One <m>1</m>",
        "Option Two <m>2</m>"
    }
}
```

#### 4. Multi-Section Array

A complex array containing multiple sections or footer text.

```lua
local text = {
    { Title = "Combat", List = { "Attack <m>1</m>", "Block <m>2</m>" } },
    { Title = "Movement", List = { "Sprint <k>SHIFT</k>" } },
    "Press <k>ESC</k> to quit" -- Footer text
}
```

***

### 💡 Examples

#### Example 1: Basic Mission Objective

Ideal for simple tracking at the top right of the screen.

```lua
exports['ak47_lib']:ShowObjective(
    "Infiltrate the Humane Labs facility.", 
    "Current Task", 
    "top"
)
```

#### Example 2: Editor or Tool Controls

Using the `center` position and sections to explain controls to a player.

```lua
local controls = {
    {
        Title = "Zone Controls",
        List = {
            "Add Point <m>1</m>",
            "Undo Last Point <m>2</m>",
            "Confirm Zone <k>ENTER</k>",
        }
    },
    {
        Title = "Camera",
        List = {
            "Rotate <m>3</m>",
            "Move <k>W</k> <k>A</k> <k>S</k> <k>D</k>",
        }
    },
    "Exit Editor <k>DEL</k>"
}

exports['ak47_lib']:ShowObjective(controls, "Editor Mode", "center")        
```

#### Example 3: Dynamic Updates

You can overwrite the current objective simply by calling the export again.

```lua
-- First Objective
exports['ak47_lib']:ShowObjective("Wait for the contact...", "The Deal")

-- Update later in the script
Citizen.SetTimeout(5000, function()
    exports['ak47_lib']:ShowObjective("Meet the contact at the pier.", "The Deal")
    PlaySoundFrontend(-1, "Mission_Pass_Notify", "DLC_HEISTS_GENERAL_FRONTEND_SOUNDS", 0)
end)
```

#### Example 4: Instruction With Buttons

```lua
Lib47.ShowObjective({
    {
        Title = "Current Mode: " .. currentMode,
        List = {}
    },
    {
        Title = "Path Nodes:",
        List = {
            "Add Node <m>1<m>",
            "Undo Node <m>2<m>",
            "Rotate Ped <m>3<m>",
            "Precision Mode <k>SHIFT<k>",
        },
    },
    {
        Title = "Node Types:",
        List = {
            "Toggle Wait (Stoppage) <k>F<k>",
            "Toggle Action (Checkout) <k>G<k>",
        },
    },
    {
        Title = "Navigation:",
        List = {
            "Complete Path <k>ENTER<k>",
            "Skip Step <k>TAB<k>",
            "Go Back <k>BACKSPACE<k>",
            "Cancel Creation <k>DEL<k>",
        }
    },
    {
        Title = "Camera Controls:",
        List = {
            "<m><m> <k>W<k> <k>A<k> <k>S<k> <k>D<k>",
            "Move UP <k>Q<k>",
            "Move Down <k>E<k>",
        },
    },
}, "NPC Navigation")
```

<div align="left"><figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FqvIQsOOsqlZwibK2kKHi%2Fimage.png?alt=media&amp;token=6d2cdb21-29b7-48ea-98a4-b0628acaa54e" alt=""><figcaption></figcaption></figure></div>

***

### 🎨 Visual Features

* Night Mode: The card background automatically dims between 21:00 and 06:00 for better visibility.
* Rich Text: Supports the same `<k>` (Key) and `<m>` (Mouse) tags as the Checklist system.
* Right-Aligned: All text and list bullets are automatically aligned to the right edge of the screen.


# Notify

The Notification System is a highly customizable HUD component featuring 6 distinct visual styles, 8 screen positions, and smart duplicate handling. It supports markdown formatting, dynamic icons, and automatic "Night Mode" theming.

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FgjuD2TozDWJd3Gyc64Ai%2Fimage.png?alt=media&amp;token=285b7816-c87c-4151-82b1-508548e0fddc" alt=""><figcaption></figcaption></figure>

***

### 🛠️ Export

#### `Notify`

Sends a notification to the player's screen.

```lua
exports['ak47_lib']:Notify(data)
-- or
Lib47.Notify(data)
```

`data` Object Properties:

| **Property**    | **Type**    | **Default** | **Description**                                                                                                                                 |
| --------------- | ----------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `description`   | String      | `nil`       | The main message body (Supports Markdown).                                                                                                      |
| `title`         | String      | "INFO"      | The header text.                                                                                                                                |
| `type`          | String      | "inform"    | Category: `'inform'`, `'success'`, `'warning'`, `'error'`.                                                                                      |
| `style`         | String      | "minimal"   | Visual theme (see [Styles](https://www.google.com/search?q=%23%F0%9F%8E%A8-styles)).                                                            |
| `position`      | String      | "top-right" | Location: `'top-left'`, `'top-right'`, `'top-center'`, `'bottom-left'`, `'bottom-right'`, `'bottom-center'`, `'center-left'`, `'center-right'`. |
| `duration`      | Number      | 3000        | Time in milliseconds before the notification fades.                                                                                             |
| `icon`          | String      | (Auto)      | FontAwesome icon class (e.g., `"fa-star"`).                                                                                                     |
| `iconAnimation` | String      | `nil`       | FA animation (e.g., `"spin"`, `"pulse"`, `"bounce"`).                                                                                           |
| `sound`         | Boolean/Str | `false`     | `true` for default sound, or a string path to a custom `.mp3`.                                                                                  |

***

### 🎨 Styles

You can change the look of the notification by passing the `style` property.

| **Style Name**  | **Description**                                                           |
| --------------- | ------------------------------------------------------------------------- |
| `minimal`       | Sleek gradient background with a circular progress ring around the icon.  |
| `frost`         | A modern "glassmorphism" pill shape.                                      |
| `frost-fade`    | Similar to frost, but with a transparent gradient fade-out effect.        |
| `glass`         | A rigid, rectangular card with sharp corners and high-end blur.           |
| `stream`        | Energetic style with a large accent bar and monospace font.               |
| `glow-dot`      | Minimalist style featuring a pulsing colored dot instead of an icon.      |
| `vertical-line` | Clean layout separated by a thin vertical line between the icon and text. |

***

### 📝 Markdown Support

The `description` field supports basic markdown formatting:

* `**Bold Text**` renders as Bold Text
* `*Italic Text*` renders as *Italic Text*
* `` `Code Block` `` renders as a highlighted code snippet.
* `\n` creates a new line.

***

### 💡 Examples

#### Example 1: Basic Success Notification

A standard success message using the default `minimal` style.

```lua
exports['ak47_lib']:Notify({
    title = "PURCHASE COMPLETE",
    description = "You bought a **New Vehicle** for `$50,000`.",
    type = "success",
    position = "top-right",
    sound = true
})
```

#### Example 2: Minimalist "Glow Dot" Style

Ideal for clean UIs where icons are too distracting.

```lua
exports['ak47_lib']:Notify({
    title = "GPS Update",
    description = "A new location has been marked on your map.",
    style = "glow-dot",
    type = "inform",
    position = "bottom-left",
    duration = 5000
})
```

#### Example 3: Complex "Stream" Style with Animation

Great for high-action alerts or system-wide broadcasts.

```lua
exports['ak47_lib']:Notify({
    title = "System Alert",
    description = "The facility is under lockdown!\n*Proceed to the nearest exit.*",
    type = "error",
    style = "stream",
    icon = "fa-radiation",
    iconAnimation = "spin",
    position = "top-center",
    duration = 10000
})
```

***

### ℹ️ Features

* Duplicate Handling: If a notification with the same title and description is sent while one is already active, a "x2" badge will appear and the timer will reset rather than cluttering the screen.
* Night Mode: Backgrounds automatically shift opacity and color based on in-game time (dimming between 21:00 and 06:00) to reduce eye strain.
* Interactive Dismiss: Players can click on any notification to dismiss it instantly.


# Input

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2F9EMVhNcel9SesBj6yPep%2Fimage.png?alt=media&amp;token=1c15a1b3-db78-4af2-9643-9f656bb92af7" alt=""><figcaption></figcaption></figure>

This function displays a customizable input dialog to the player. It is a wrapper that automatically switches between your custom React interface (`Interface.ShowInput`) based on your `Config.InputDialog` setting.

### Syntax

```lua
Lib47.ShowInput(heading, rows, options)
```

#### Parameters

| **Argument** | **Type** | **Description**                                                           |
| ------------ | -------- | ------------------------------------------------------------------------- |
| `heading`    | String   | The title text displayed at the top of the dialog.                        |
| `rows`       | Table    | An array of tables, where each table represents an input field.           |
| `options`    | Table    | (Optional) A table containing global configuration for the dialog window. |

#### Return Value

* Success: Returns a `table` (array) containing the input values in the order of the rows.
* Cancel: Returns `nil` if the user cancels the dialog (presses Escape or the Cancel button).

***

### Row Properties

Each entry in the `rows` table can contain the following properties.

#### Common Properties (All Types)

| **Property**  | **Type** | **Default** | **Description**                                             |
| ------------- | -------- | ----------- | ----------------------------------------------------------- |
| `type`        | String   | `'input'`   | The type of input field (see list below).                   |
| `label`       | String   | -           | The text label displayed above the input.                   |
| `description` | String   | -           | Small helper text displayed below the input.                |
| `icon`        | String   | -           | FontAwesome icon class (e.g., `'fa-user'`).                 |
| `required`    | Boolean  | `false`     | If true, the user cannot submit without filling this field. |
| `disabled`    | Boolean  | `false`     | If true, the field is visible but not editable.             |
| `default`     | Mixed    | -           | The initial value of the field.                             |
| `placeholder` | String   | -           | Placeholder text inside the input field.                    |

#### Input Types

| **Type**         | **Specific Properties**                | **Description**                 |
| ---------------- | -------------------------------------- | ------------------------------- |
| `'input'`        | `minLength`, `maxLength`, `password`   | Standard text input.            |
| `'textarea'`     | `min` (rows), `max` (rows), `autosize` | Multi-line text area.           |
| `'number'`       | `min`, `max`, `step`, `precision`      | Numeric input.                  |
| `'slider'`       | `min`, `max`, `step`                   | A visual range slider.          |
| `'select'`       | `options`, `clearable`, `searchable`   | Single-choice dropdown.         |
| `'multi-select'` | `options`, `maxSelectedValues`         | Multiple-choice dropdown/chips. |
| `'checkbox'`     | -                                      | A boolean toggle switch.        |
| `'date'`         | `format`, `returnString`               | Date picker.                    |
| `'time'`         | -                                      | Time picker.                    |
| `'date-range'`   | `format`, `returnString`               | Start and End date picker.      |
| `'color'`        | -                                      | RGB Hex color picker.           |

> Note: For `select` and `multi-select`, the `options` property must be an array of objects structured as `{ value = "val", label = "Label" }`.

***

### Dialog Options

The third argument, `options`, controls the window's appearance and behavior.

| **Option**    | **Type**    | **Default** | **Description**                                       |
| ------------- | ----------- | ----------- | ----------------------------------------------------- |
| `allowCancel` | Boolean     | `true`      | Whether the generic "Cancel" button is shown.         |
| `size`        | String      | `'md'`      | Window width: `'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`. |
| `borders`     | Table/Array | -           | Add accent borders. Example: `{'left', 'right'}`.     |
| `colors`      | Table       | -           | Custom coloring for this specific dialog instance.    |

**Colors Structure**

```lua
colors = {
    colorPrimary = "rgba(15, 15, 20, 0.85)", -- Background
    colorSecondary = "#FFD700",              -- Accent/Buttons
    colorText = "#ffffff"                    -- Text Color
}
```

***

### Examples

#### 1. Basic Information Form

A simple form to get a player's name and age.

```lua
local input = Lib47.ShowInput('Personal Information', {
    {
        type = 'input',
        label = 'Full Name',
        description = 'Enter your character name',
        required = true,
        icon = 'fa-user'
    },
    {
        type = 'number',
        label = 'Age',
        default = 18,
        min = 18,
        max = 100,
        icon = 'fa-calendar-days'
    },
    {
        type = 'textarea',
        label = 'Bio',
        placeholder = 'Tell us about yourself...',
        autosize = true
    }
})

if input then
    local name = input[1]
    local age = input[2]
    local bio = input[3]
    print('Result:', name, age, bio)
end
```

#### 2. Advanced Selection & Formatting

Using dropdowns, date ranges, and sliders with specific styling.

```lua
local options = {
    { value = 'police', label = 'Police Department' },
    { value = 'ems', label = 'Medical Services' },
    { value = 'mechanic', label = 'Mechanic' }
}

local input = Lib47.ShowInput('Job Application', {
    {
        type = 'select',
        label = 'Select Department',
        options = options,
        searchable = true, -- Allows typing to filter options
        required = true
    },
    {
        type = 'multi-select',
        label = 'Availability',
        options = {
            { value = 'mon', label = 'Monday' },
            { value = 'tue', label = 'Tuesday' },
            { value = 'wed', label = 'Wednesday' }
        },
        maxSelectedValues = 2
    },
    {
        type = 'date-range',
        label = 'Vacation Period',
        format = 'DD/MM/YYYY', -- UI will return formatted string
        returnString = true
    },
    {
        type = 'slider',
        label = 'Experience Level',
        min = 1,
        max = 10,
        step = 1,
        default = 5
    }
}, {
    size = 'lg', -- Large window
    allowCancel = true
})

if input then
    -- input[1] is the selected value string (e.g., 'police')
    -- input[2] is a table/array (e.g., {'mon', 'wed'})
    -- input[3] is a table/array (e.g., {'20/10/2023', '25/10/2023'})
    -- input[4] is a number (e.g., 5)
end
```

#### 3. Custom Themed Dialog

Override the default colors for a specific menu (e.g., a dark/red illegal shop).

```lua
local input = Lib47.ShowInput('Black Market', {
    {
        type = 'input',
        label = 'Secret Code',
        password = true, -- Hides characters
        icon = 'fa-lock'
    },
    {
        type = 'color',
        label = 'Vehicle Neons',
        default = '#ff0000'
    }
}, {
    size = 'sm',
    borders = {'top', 'bottom'}, -- Only borders on top and bottom
    colors = {
        colorPrimary = "rgba(10, 0, 0, 0.95)",
        colorSecondary = "#ff3333", -- Red accent
        colorText = "#ffcccc"
    }
})
```


# Alert

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2F8dvTahRAj6InIkL5Yez8%2F3zUgw2p0ma.png?alt=media&amp;token=2f2b9467-b02d-49a1-969f-535f3b575af1" alt=""><figcaption></figcaption></figure>

This resource provides a synchronous (blocking) Alert Dialog system. It utilizes `Citizen.Await` to pause code execution until the user interacts with the alert, making logic flows linear and easy to write.

### ⚙️ Exports

You can access these functions from any other resource using the `exports` handler.

#### `ShowAlert`

Opens a customizable alert dialog and awaits a user response.

```lua
local result = exports['ak47_lib']:ShowAlert(data)
```

Parameters (`data` table):

| **Property** | **Type**  | **Default** | **Description**                                                                                     |
| ------------ | --------- | ----------- | --------------------------------------------------------------------------------------------------- |
| `header`     | `string`  | `nil`       | The title text of the alert.                                                                        |
| `content`    | `string`  | `nil`       | The body text. Supports `\n` for new lines and Markdown (e.g., `**bold**`).                         |
| `centered`   | `boolean` | `false`     | If `true`, centers the text within the dialog.                                                      |
| `cancel`     | `boolean` | `false`     | If `true`, displays a "Cancel" button alongside the confirm button.                                 |
| `size`       | `string`  | `config`    | The width of the dialog. Options usually include `'sm'`, `'md'`, `'lg'`.                            |
| `labels`     | `table`   | `nil`       | Custom text for buttons. See [Label Object](https://www.google.com/search?q=%23label-object) below. |
| `colors`     | `table`   | `config`    | Custom Hex colors. See [Color Object](https://www.google.com/search?q=%23color-object) below.       |
| `borders`    | `table`   | `config`    | Array of border positions (e.g., `{'bottom'}`).                                                     |

Return Value:

* `'confirm'`: User clicked the confirm button.
* `'cancel'`: User clicked cancel, the alert was overwritten, or the UI was forcibly closed.

> Note: If an alert is already active when `ShowAlert` is called, the new function call will immediately return `'cancel'` to prevent overlapping UIs.

***

#### `HideAlert`

Forcibly closes the currently active alert (if any) and resolves its promise as `'cancel'`.

```lua
exports['ak47_lib']:HideAlert()
```

***

### 🧩 Data Objects

#### Label Object

Used inside the `labels` parameter to change button text.

```lua
{
    confirm = "Yes, Do it", -- Text for the primary button
    cancel = "No, Go back"  -- Text for the secondary button (if cancel = true)
}
```

#### Color Object

Used inside the `colors` parameter to override default styling.

```lua
{
    colorPrimary = '#1F1F1F',   -- Background color
    colorSecondary = '#FF5555', -- Accent/Button color
    colorText = '#FFFFFF'       -- Text color
}
```

***

### 💡 Examples

#### Example 1: Basic Information

A simple notification that pauses the script until acknowledged.

```lua
local result = exports['ak47_lib']:ShowAlert({
    header = 'Welcome',
    content = 'Welcome to the server! Please read the rules.',
    centered = true,
    size = 'sm'
})

print('User acknowledged the alert.')
```

#### Example 2: Confirmation Logic

Using the return value to determine the next step.

```lua
local choice = exports['ak47_lib']:ShowAlert({
    header = 'Purchase Vehicle',
    content = 'Are you sure you want to buy this vehicle for **$50,000**?',
    cancel = true, -- Enable cancel button
    labels = {
        confirm = 'Purchase',
        cancel = 'Walk Away'
    }
})

if choice == 'confirm' then
    TriggerServerEvent('buyVehicle')
    print('Vehicle purchased')
else
    print('Purchase cancelled')
end
```

#### Example 3: Danger/Warning Style

Customizing colors to indicate a dangerous action (red styling).

```lua
local confirmation = exports['ak47_lib']:ShowAlert({
    header = 'DELETE CHARACTER',
    content = 'This action is **irreversible**.\nAre you sure?',
    centered = true,
    cancel = true,
    size = 'md',
    colors = {
        colorPrimary = '#360000', -- Dark Red Background
        colorSecondary = '#FF0000', -- Bright Red Buttons
        colorText = '#FFFFFF'
    },
    borders = {'bottom', 'top'},
    labels = { confirm = 'PERMANENTLY DELETE', cancel = 'Cancel' }
})

if confirmation == 'confirm' then
    -- Delete logic here
end
```

#### Example 4: Handling Overlaps

Since the function returns immediately if another alert is open, you can handle that edge case.

```lua
local status = exports['ak47_lib']:ShowAlert({ header = 'Test' })

if status == 'cancel' then
    -- Note: This could mean the user clicked cancel OR 
    -- simply that the UI was already open.
    print('Alert closed or could not open.')
end
```


# Gizmo

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2Ft7rvMFS9Yfl1sUotUd0q%2Fimage.png?alt=media&amp;token=68d770e6-f838-48b5-bf8f-ba0e39e7ced2" alt=""><figcaption></figcaption></figure>

***

### 📦 Exports

#### `StartGizmo`

Initializes the 3D Gizmo interface, spawning a dummy entity (if provided) and locking focus to the NUI for precise manipulation.

Syntax:

```lua
exports['ak47_lib']:StartGizmo(options, callback)
```

Parameters:

| **Parameter** | **Type**   | **Required** | **Description**                                                                   |
| ------------- | ---------- | ------------ | --------------------------------------------------------------------------------- |
| `options`     | `table`    | Yes          | Configuration table for the Gizmo deployment.                                     |
| `callback`    | `function` | Yes          | Triggers continuously on positional updates, and once upon closing or cancelling. |

`options` Table Breakdown:

| **Key**  | **Type**          | **Default**      | **Description**                                           |
| -------- | ----------------- | ---------------- | --------------------------------------------------------- |
| `model`  | `string` / `hash` | `nil`            | The model name or hash to spawn as the visual dummy prop. |
| `coords` | `vector3`         | N/A              | The starting coordinates for the Gizmo.                   |
| `rot`    | `vector3`         | `vector3(0,0,0)` | The starting rotation for the Gizmo.                      |

Callback Event Types (`result.event`):

* `update`: Fires constantly as the player moves the object via the NUI. Returns raw axis data (`x`, `y`, `z`, `rotX`, `rotY`, `rotZ`).
* `closed`: Fires when the player confirms the placement. Returns `coords` and `rot` as `vector3`.
* `cancelled`: Fires when the player cancels the placement. Returns the *initial* starting `coords` and `rot` as `vector3`.

***

#### `StopGizmo`

Programmatically stops the active Gizmo session. This is typically handled internally by the NUI, but can be forced via code if required (e.g., if a player is killed or restrained while placing an item).

Syntax:

```lua
exports['ak47_lib']:StopGizmo(isCancel)
```

Parameters:

| **Parameter** | **Type**  | **Required** | **Description**                                                                                                  |
| ------------- | --------- | ------------ | ---------------------------------------------------------------------------------------------------------------- |
| `isCancel`    | `boolean` | Yes          | `true` aborts placement and returns the object to its starting position. `false` confirms the current placement. |

***

### 🎮 Player Controls

When the Gizmo is active, the library handles the following native inputs automatically to ensure a smooth user experience:

* Right Mouse Button (Hold): Temporarily hides the cursor and disables player firing/aiming, allowing the user to free-look with the camera while keeping the Gizmo open.
* Bounding Box: A visual 3D bounding box automatically renders around the active dummy prop to help users visualize the physical footprint.

***

### 💻 Complete Example Implementation

Below is a fully functional client-side script utilizing the Gizmo export to spawn, move, and finalize a networked prop.

```lua
local isPlacingObject = false
local finalCoords = nil
local finalRotation = nil

-- Function to handle the spawning of the actual server-sided object
local function SpawnFinalNetworkedObject(model, coords, rot)
    local hash = type(model) == 'string' and joaat(model) or model
    RequestModel(hash)
    
    while not HasModelLoaded(hash) do 
        Wait(0) 
    end
    
    local obj = CreateObject(hash, coords.x, coords.y, coords.z, true, true, false)
    SetEntityRotation(obj, rot.x, rot.y, rot.z, 2, true)
    FreezeEntityPosition(obj, true)
end

-- Main function to trigger the Gizmo
function StartPlacingProp(modelName)
    if isPlacingObject then return end
    isPlacingObject = true
    
    local playerPed = PlayerPedId()
    local startCoords = GetEntityCoords(playerPed) + GetEntityForwardVector(playerPed) * 2.0
    local startHeading = GetEntityHeading(playerPed)
    
    -- Initialize the Gizmo via ak47_lib
    exports['ak47_lib']:StartGizmo({
        model = modelName,
        coords = startCoords,
        rot = vector3(0.0, 0.0, startHeading)
    }, function(result)
        
        -- Handle real-time movement updates (Useful for zone/distance checks)
        if result.event == 'update' then
            -- Example: print("Moving to:", result.x, result.y, result.z)
            return
        end
        
        -- Handle cancellation
        if result.event == 'cancelled' then
            isPlacingObject = false
            print("Placement cancelled by user.")
            return
        end
        
        -- Handle the final confirmed placement
        if result.event == 'closed' then
            isPlacingObject = false
            finalCoords = result.coords
            finalRotation = result.rot
            
            print("Finished placing prop!")
            print("Final Coords:", finalCoords)
            print("Final Rotation:", finalRotation)
            
            -- Spawn the persistent object
            SpawnFinalNetworkedObject(modelName, finalCoords, finalRotation)
        end
    end)
end

-- Test Command
RegisterCommand('testgizmo', function()
    StartPlacingProp('prop_bench_01a')
end, false)
```


# Events


# Client

The `ak47_lib` provides unified client events that trigger across all supported frameworks (ESX, QBCore, Qbox). This allows you to listen for core player state changes without writing framework-specific event handlers.

#### **`ak47_lib:OnPlayerLoaded`**

Triggered when the player's character has completely loaded into the server and spawned. This event also safely fires if your resource is restarted while the player is already loaded in the server, ensuring your script always catches the active player state.

```lua
--- @param PlayerData table The full framework-specific player data object
--- @param resourceName string|nil The name of the resource if triggered by a resource restart
AddEventHandler('ak47_lib:OnPlayerLoaded', function(PlayerData, resourceName)
    print("Player has successfully loaded!")
    
    -- Example: Initialize script data or spawn UI elements
    Lib47.PlayerLoaded = true
    
    -- If you need to check framework-specific data
    if Lib47.Framework == 'qb' or Lib47.Framework == 'qbx' then
        print("Citizen ID: " .. PlayerData.citizenid)
    elseif Lib47.Framework == 'esx' then
        print("Identifier: " .. PlayerData.identifier)
    end
end)
```

#### **`ak47_lib:OnPlayerUnload`**

Triggered on the client when a player logs out or unloads their character (e.g., returning to the character selection screen). This event is standardized and dispatched across all supported frameworks (`qb-core`, `qbx_core`, and `es_extended`).

```lua
AddEventHandler('ak47_lib:OnPlayerUnload', function()
    print("Player has logged out or unloaded.")
    
    -- Example: Clean up client-side targets, blips, active loops, or NUI elements
    Lib47.PlayerLoaded = false
end)
```

#### **`ak47_lib:OnJobUpdate`**

Triggered on the client whenever the player's job (or grade) is updated by the server.

```lua
--- @param JobInfo table The updated framework-specific job object
AddEventHandler('ak47_lib:OnJobUpdate', function(JobInfo)
    print("Player's job has been updated!")
    
    -- Note: Because the raw JobInfo varies by framework, it's highly recommended
    -- to use the built-in Lib47.GetJob() function inside this event to get a standardized table.
    
    local formattedJob = Lib47.GetJob()
    if formattedJob then
        print(string.format("New Job: %s | Grade: %s | Is Boss: %s", 
            formattedJob.label, 
            formattedJob.grade.name, 
            tostring(formattedJob.isboss)
        ))
    end
end)
```

#### **`ak47_lib:OnPlayerDataUpdate`**

Triggered on the client whenever the core player data object receives a synchronized update from the server. This often occurs when inventory items change, metadata updates, or money is added/removed.

```lua
--- @param PlayerData table The newly updated framework-specific player data object
AddEventHandler('ak47_lib:OnPlayerDataUpdate', function(PlayerData)
    -- This event fires frequently depending on the framework and server activity.
    -- Use this to dynamically update UI elements or active tracking loops.
    
    if Lib47.Framework == 'esx' then
        -- Example: Checking updated accounts in ESX
        for i=1, #PlayerData.accounts, 1 do
            if PlayerData.accounts[i].name == 'bank' then
                print("Updated Bank Balance: $" .. PlayerData.accounts[i].money)
            end
        end
        
    elseif Lib47.Framework == 'qb' or Lib47.Framework == 'qbx' then
        -- Example: Checking updated metadata in QBCore/Qbox
        print("Updated Hunger Level: " .. PlayerData.metadata["hunger"])
    end
end)
```

### **`ak47_lib:OnRemoveItem`**

Triggered on the client whenever an item is removed from the player's inventory or its total quantity decreases. This event automatically calculates inventory state changes across supported frameworks to ensure reliable tracking of lost or used items.

```lua
--- @param itemName string The spawn name of the item that was removed
--- @param newAmount number The new total amount of this item remaining in the inventory
AddEventHandler('ak47_lib:OnRemoveItem', function(itemName, newAmount)
    -- Example: Check if the player lost a specific required item
    if itemName == 'id_card' and newAmount == 0 then
        print("Player no longer has their ID card!")
    end
    
    -- Example: Tracking general item usage or removal
    print(string.format("Item removed: %s | Quantity remaining: %s", itemName, newAmount))
    
    -- Note: This is useful for disabling active loops or hiding UI elements 
    -- if the player drops or uses an item your script relies on.
end)
```


# Imports


# Callback

The `ak47_lib` features a powerful, two-way callback system built on promises. This allows you to easily request data from the Server to the Client, or from the Client to the Server, yielding the thread until a response is received or a timeout occurs.

You can access these functions via the `Lib47.Callback` namespace.

### Client to Server Callbacks

These functions are used when a Client script needs to request data from the Server.

#### `Lib47.Callback.Register` (Server-Side)

Registers a server-side callback that can be triggered by clients. Duplicate registrations will output an error to the console to prevent overlapping event handlers.

```lua
--- @param name string The unique name of the callback
--- @param cb function The function to execute when called. The first parameter is ALWAYS the player's source.
Lib47.Callback.Register('ak47_lib:server:getPlayerData', function(source, arg1, arg2)
    -- The first argument is automatically the invoking player's server ID
    local player = Lib47.GetPlayer(source)
    local money = Lib47.GetMoney(source, 'cash')
    
    -- You can return a single value, multiple values, or nil
    return money, player.job.name
end)
```

#### `Lib47.Callback.Await` (Client-Side)

Triggers a registered server callback and waits for the response. If the server takes longer than the configured timeout (defaulting to 15 seconds), it will automatically resolve as `nil` and print a timeout warning.

```lua
--- @param name string The name of the callback to trigger
--- @param _ nil The second argument is ignored on the client (used for target parity)
--- @param ... any Any additional arguments you want to pass to the server
--- @return ... any Returns the unpacked values sent back by the server
local cashBalance, jobName = Lib47.Callback.Await('ak47_lib:server:getPlayerData', nil, "extra_arg1", "extra_arg2")

if cashBalance then
    print(("I have $%s and I work as a %s"):format(cashBalance, jobName))
else
    print("Server failed to respond in time.")
end
```

### Server to Client Callbacks

These functions are used when the Server needs to request data from a specific Client (e.g., checking their UI state, getting a waypoint coordinate, or verifying a client-side entity).

#### `Lib47.Callback.Register` (Client-Side)

Registers a client-side callback that can be triggered by the server.

```lua
--- @param name string The unique name of the callback
--- @param cb function The function to execute when called by the server.
Lib47.Callback.Register('ak47_lib:client:getWaypointCoordinates', function(arg1)
    -- Execute client-side logic
    local waypoint = GetFirstBlipInfoId(8)
    
    if DoesBlipExist(waypoint) then
        local coords = GetBlipInfoIdCoord(waypoint)
        return coords -- Return data to the server
    end
    
    return nil
end)
```

#### `Lib47.Callback.Await` (Server-Side)

Triggers a registered client callback on a specific player and waits for the response. If the target player doesn't exist or fails to respond within the timeout, it resolves to `nil`.

```lua
--- @param name string The name of the callback to trigger
--- @param target number The server ID of the player you are requesting data from
--- @param ... any Any additional arguments you want to pass to the client
--- @return ... any Returns the unpacked values sent back by the client
local targetPlayerId = 1
local waypointCoords = Lib47.Callback.Await('ak47_lib:client:getWaypointCoordinates', targetPlayerId, "optional_arg")

if waypointCoords then
    print("Player's waypoint is at: " .. json.encode(waypointCoords))
else
    print("Player does not have a waypoint or failed to respond.")
end
```


# ak47\_target

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FhVp1NxKIzkbTVfXg4v1P%2Ftarget.png?alt=media&amp;token=8f79cec2-9163-4735-9a34-bb926d3932a0" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FVLnaYrEbUTq7H1btxkGl%2FmxfheaIPxA.png?alt=media&amp;token=34eaf933-4d7b-4cac-8ade-2eac9618d5f9" alt=""><figcaption></figcaption></figure>

ak47\_target is designed to be a seamless, 100% drop-in replacement for `ox_target`, `qb-target`, and `qtarget`.

You do not need to rewrite your existing scripts or change your exports. `ak47_target` natively intercepts the export calls meant for those older resources and automatically translates them into its own optimized format.

#### Supported Scripts

* ox\_target (Fully supported via `ox_target` export handlers)
* qb-target (Fully supported via `qb-target` export handlers)
* qtarget (Fully supported via `qtarget` export handlers)

#### Installation

1. Download the latest release from the repository.
2. Extract the `ak47_target` folder into your server's `resources` directory.
3. Ensure you completely stop and remove any existing targeting scripts (e.g., `qb-target`, `ox_target`).
4. Add `start ak47_target` after the framework (es\_extended/qb-core/qbx\_core) to your `server.cfg`.

#### How it Works

Under the hood, `ak47_target` registers event handlers for `__cfx_export_ox_target_%s`, `__cfx_export_qb-target_%s`, and `__cfx_export_qtarget_%s`.

This means if you have an older script that calls:

```lua
-- An old qb-target export
exports['qb-target']:AddBoxZone("BankZone", vector3(100.0, 100.0, 30.0), 2.0, 2.0, {
    name = "BankZone",
    heading = 0.0,
    debugPoly = false,
    minZ = 28.0,
    maxZ = 32.0,
}, {
    options = {
        {
            type = "client",
            event = "bank:open",
            icon = "fas fa-university",
            label = "Open Bank",
            job = "all"
        }
    },
    distance = 2.5
})
```

...`ak47_target` will automatically catch this, format the legacy options (like converting `job` to `groups`, or `type = "client"` to standard events), and register it as an `ak47_target` box zone.

#### Legacy Functions Handled

All standard functions from the supported target scripts are automatically redirected, including:

* `AddBoxZone` / `AddPolyZone` / `AddCircleZone`
* `AddTargetModel` / `RemoveTargetModel`
* `AddTargetEntity` / `RemoveTargetEntity`
* `AddGlobalPed` / `AddGlobalVehicle` / `AddGlobalObject` / `AddGlobalPlayer`
* And their respective `ox_target` equivalents like `addBoxZone`, `addGlobalPed`, `removeZone`, etc.

*(Note: The deprecated `AddEntityZone` from `qb-target` is also safely re-routed to `AddTargetEntity` with a console warning)*.


# Api

#### 🛠️ The `options` Table

Whenever you add a target (to an entity, model, zone, or globally), you must pass an `options` table. This defines what the player sees and what happens when they click the option.

**Option Parameters**

| **Parameter** | **Type**           | **Description**                                                                           |
| ------------- | ------------------ | ----------------------------------------------------------------------------------------- |
| `name`        | `string`           | A unique identifier for the option.                                                       |
| `label`       | `string`           | The text displayed in the target menu.                                                    |
| `icon`        | `string`           | The FontAwesome icon class (e.g., `'fas fa-car'`).                                        |
| `description` | `string`           | (Optional) Extra description text below the label.                                        |
| `distance`    | `number`           | The maximum distance the player can interact from.                                        |
| `bones`       | `string` / `table` | (Optional) Specific entity bones the player must look at.                                 |
| `offset`      | `vector3`          | (Optional) Offset from the entity's center.                                               |
| `groups`      | `string` / `table` | (Optional) Restrict to specific jobs, gangs, or citizen IDs.                              |
| `items`       | `string` / `table` | (Optional) Restrict to players carrying specific item(s).                                 |
| `anyItem`     | `boolean`          | (Optional) If `true`, the player only needs *one* of the specified items.                 |
| `canInteract` | `function`         | (Optional) A custom check function returning `true` or `false`.                           |
| `submenu`     | `table`            | (NEW) (Optional) A nested table of target options to create a multi-level drop-down menu. |

**Action Handlers (Pick One)**

You must define one of the following to execute an action when the player clicks the option (unless the option is just a parent for a `submenu`):

* `onSelect` or `action`: A direct Lua callback function. Receives a `data` table `(entity, coords, distance, zone)`.
* `event`: Triggers a Client Event.
* `serverEvent`: Triggers a Server Event (automatically converts entity to `netId`).
* `command`: Executes a console command.
* `export`: Calls an export formatted as `'resourceName.exportName'`.

***

#### 📂 Submenus (Nested Options)

You can now nest target options inside one another to create clean, organized submenus. Options containing a `submenu` table will act as a folder and do not need an action handler (like `onSelect`).

You can nest submenus as deeply as you want!

**Example Submenu Table**

```lua
local myOptions = {
    {
        name = "vehicle_options",
        icon = "fas fa-car",
        label = "Vehicle Options",
        distance = 2.5,
        -- Level 1 Submenu
        submenu = {
            {
                name = "inspect_vehicle",
                icon = "fas fa-magnifying-glass",
                label = "Inspect Vehicle",
                distance = 2.5,
                onSelect = function(data)
                    print("Inspecting vehicle entity ID: " .. tostring(data.entity))
                end
            },
            {
                name = "door_controls",
                icon = "fas fa-door-open",
                label = "Door Controls",
                distance = 2.5,
                -- Level 2 Submenu (Nested inside Level 1)
                submenu = {
                    {
                        name = "toggle_hood",
                        icon = "fas fa-angle-up",
                        label = "Toggle Hood",
                        distance = 2.5,
                        onSelect = function(data)
                            print("Toggling hood for vehicle: " .. tostring(data.entity))
                        end
                    },
                    {
                        name = "toggle_trunk",
                        icon = "fas fa-angle-down",
                        label = "Toggle Trunk",
                        distance = 2.5,
                        onSelect = function(data)
                            print("Toggling trunk for vehicle: " .. tostring(data.entity))
                        end
                    }
                }
            }
        }
    }
}

-- Adding the submenu to all vehicles
exports.ak47_target:addGlobalVehicle(myOptions)
```

***

#### 🌍 Global Targets

Global targets apply to *every* entity of a specific type across the entire map.

**`addGlobalPed`**

Adds options to all Peds.

```lua
exports.ak47_target:addGlobalPed(options)
```

**`addGlobalVehicle`**

Adds options to all Vehicles.

```lua
exports.ak47_target:addGlobalVehicle(options)
```

**`addGlobalObject`**

Adds options to all Objects.

```lua
exports.ak47_target:addGlobalObject(options)
```

**`addGlobalPlayer`**

Adds options to all other Players.

```lua
exports.ak47_target:addGlobalPlayer(options)
```

**`addGlobalOption`**

Adds an option anywhere in the world (catches world coordinates if no entity is hit).

```lua
exports.ak47_target:addGlobalOption(options)
```

***

#### 📦 Model & Entity Targets

**`addModel`**

Adds options to specific entity models (props, vehicle models, ped models).

```lua
-- Models can be a single string/hash or a table of strings/hashes
local models = { 'prop_atm_01', 'prop_atm_02', `prop_atm_03` }

exports.ak47_target:addModel(models, {
    {
        name = 'use_atm',
        icon = 'fas fa-credit-card',
        label = 'Use ATM',
        distance = 1.5,
        event = 'myBank:client:openMenu'
    }
})
```

**`addEntity` (Networked)**

Adds options to specific networked entities using their `netId`.

```lua
exports.ak47_target:addEntity(netId, options) -- Accepts single netId or table of netIds
```

**`addLocalEntity` (Non-Networked)**

Adds options to specific local entities using their local entity ID.

```lua
exports.ak47_target:addLocalEntity(entityId, options) -- Accepts single ID or table of IDs
```

***

#### 📍 Zone Targets

Zones allow you to define 3D areas in the world that act as targets without needing a physical prop.

**`addSphereZone`**

Creates a spherical interaction zone. Returns a unique Zone ID.

```lua
local zoneId = exports.ak47_target:addSphereZone({
    coords = vector3(100.0, -100.0, 30.0),
    radius = 2.5,
    debug = false, -- Set to true to see the zone outline
    options = {
        {
            name = 'sphere_interact',
            icon = 'fas fa-hand',
            label = 'Interact Sphere',
            distance = 2.5,
            action = function(data) print("Sphere clicked!") end
        }
    }
})
```

**`addBoxZone`**

Creates a rectangular interaction box. Returns a unique Zone ID.

```lua
local zoneId = exports.ak47_target:addBoxZone({
    coords = vector3(120.0, -120.0, 30.0),
    size = vector3(3.0, 2.0, 2.0), -- Width, Length, Height
    rotation = 45.0, -- Heading
    debug = false,
    options = { ... }
})
```

**`addPolyZone`**

Creates a custom polygon interaction zone. Returns a unique Zone ID.

```lua
local zoneId = exports.ak47_target:addPolyZone({
    points = {
        vector3(10.0, 10.0, 0.0),
        vector3(15.0, 10.0, 0.0),
        vector3(15.0, 15.0, 0.0),
        vector3(10.0, 15.0, 0.0)
    },
    thickness = 2.0,
    minZ = 28.0,
    maxZ = 32.0,
    debug = true,
    options = { ... }
})
```

***

#### 🗑️ Removing Targets

Use these exports to clean up targets, especially inside `onResourceStop` handlers.

* `removeGlobalPed(labels)`
* `removeGlobalVehicle(labels)`
* `removeGlobalObject(labels)`
* `removeGlobalPlayer(labels)`
* `removeGlobalOption(labels)`

*Note: `labels` can be a single string (the option name or label) or a table of strings.*

Removing Models & Entities:

```lua
exports.ak47_target:removeModel({'prop_atm_01'}, {'Use ATM'})
exports.ak47_target:removeEntity(netId, {'Rob Ped'})
exports.ak47_target:removeLocalEntity(entityId, {'Rob Ped'})
```

Removing Zones (Pass the Zone ID returned when you created it):

```lua
exports.ak47_target:removeZone(zoneId)
```

***

#### ⚙️ Utility Exports

**`disableTargeting`**

Temporarily disable or re-enable the targeting eye.

```lua
exports.ak47_target:disableTargeting(true) -- Disables targeting and closes UI
exports.ak47_target:disableTargeting(false) -- Re-enables targeting
```

**`isDisabled`**

Check if targeting is currently disabled.

```lua
local disabled = exports.ak47_target:isDisabled()
```

**`zoneExists`**

Check if a specific zone ID is currently active.

```lua
local exists = exports.ak47_target:zoneExists(zoneId)
```


# ak47\_inventory

{% embed url="<https://youtu.be/eAAybpZLjjI>" %}

### Photo Tool:&#x20;

{% embed url="<https://youtu.be/Gxd0MalTI8s>" %}

### Portable Weapon Repair:

{% embed url="<https://youtu.be/woqXDSIuOzc>" %}


# Installation

### Add dependencies:

1. ox\_lib: <https://github.com/overextended/ox_lib/releases/latest>

### **Step 1:**

1. Download `ak47_inventory` from your keymaster.
2. Add the script into your resources folder.
3. `start ak47_inventory` in server.cfg after `es_extended/qb-core` and make sure all other scripts are starting after the inventory script.
4. Set discord webhook in `ak47_inventory/webhooks.lua`

### Step 2:

* Remove `esx_inventory` (ESX)
* Remove `qb-shops, qb-weapons, qb-inventory` (Qbcore)

### Step 2: (QBCore)

Skip this step if you are using ESX framework.

#### **Clothing As Item Modification**

{% tabs %}
{% tab title="qb-clothing" %}

```lua
--Add this at the bottom of qb-clothing/client/main.lua

RegisterNetEvent('qb-clothing:client:loadOutfit2', function(oData)
    local ped = PlayerPedId()

    local data = oData.outfitData

    if typeof(data) ~= "table" then data = json.decode(data) end

    for k in pairs(data) do
        skinData[k].item = data[k].item
        skinData[k].texture = data[k].texture

        -- To secure backwards compability for facemixing
        if data[k].shapeMix then
            skinData[k].shapeMix = data[k].shapeMix
        end

        if data[k].skinMix then
            skinData[k].skinMix = data[k].skinMix
        end
    end

    -- Pants
    if data["pants"] ~= nil then
        SetPedComponentVariation(ped, 4, data["pants"].item, data["pants"].texture, 0)
    end

    -- Arms
    if data["arms"] ~= nil then
        SetPedComponentVariation(ped, 3, data["arms"].item, data["arms"].texture, 0)
    end

    -- T-Shirt
    if data["t-shirt"] ~= nil then
        SetPedComponentVariation(ped, 8, data["t-shirt"].item, data["t-shirt"].texture, 0)
    end

    -- Vest
    if data["vest"] ~= nil then
        SetPedComponentVariation(ped, 9, data["vest"].item, data["vest"].texture, 0)
    end

    -- Torso 2
    if data["torso2"] ~= nil then
        SetPedComponentVariation(ped, 11, data["torso2"].item, data["torso2"].texture, 0)
    end

    -- Shoes
    if data["shoes"] ~= nil then
        SetPedComponentVariation(ped, 6, data["shoes"].item, data["shoes"].texture, 0)
    end

    -- Bag
    if data["bag"] ~= nil then
        SetPedComponentVariation(ped, 5, data["bag"].item, data["bag"].texture, 0)
    end

    -- Badge
    if data["decals"] ~= nil then
        SetPedComponentVariation(ped, 10, data["decals"].item, data["decals"].texture, 0)
    end

    -- Accessory
    if data["accessory"] ~= nil then
        if QBCore.Functions.GetPlayerData().metadata["tracker"] then
            SetPedComponentVariation(ped, 7, 13, 0, 0)
        else
            SetPedComponentVariation(ped, 7, data["accessory"].item, data["accessory"].texture, 0)
        end
    else
        if QBCore.Functions.GetPlayerData().metadata["tracker"] then
            SetPedComponentVariation(ped, 7, 13, 0, 0)
        else
            SetPedComponentVariation(ped, 7, -1, 0, 2)
        end
    end

    -- Mask
    if data["mask"] ~= nil then
        SetPedComponentVariation(ped, 1, data["mask"].item, data["mask"].texture, 0)
    end

    -- Bag
    if data["bag"] ~= nil then
        SetPedComponentVariation(ped, 5, data["bag"].item, data["bag"].texture, 0)
    end

    -- Hat
    if data["hat"] ~= nil then
        if data["hat"].item ~= -1 and data["hat"].item ~= 0 then
            SetPedPropIndex(ped, 0, data["hat"].item, data["hat"].texture, true)
        else
            ClearPedProp(ped, 0)
        end
    end

    -- Glass
    if data["glass"] ~= nil then
        if data["glass"].item ~= -1 and data["glass"].item ~= 0 then
            SetPedPropIndex(ped, 1, data["glass"].item, data["glass"].texture, true)
        else
            ClearPedProp(ped, 1)
        end
    end

    -- Ear
    if data["ear"] ~= nil then
        if data["ear"].item ~= -1 and data["ear"].item ~= 0 then
            SetPedPropIndex(ped, 2, data["ear"].item, data["ear"].texture, true)
        else
            ClearPedProp(ped, 2)
        end
    end

    if data["watch"] ~= nil then
        if data["watch"].item ~= -1 and data["watch"].item ~= 0 then
            SetPedPropIndex(ped, 6, data["watch"].item, data["watch"].texture, true)
        else
            ClearPedProp(ped, 6)
        end
    end

    -- Bracelet
    if data["bracelet"] ~= nil then
        if data["bracelet"].item ~= -1 and data["bracelet"].item ~= 0 then
            SetPedPropIndex(ped, 7, data["bracelet"].item, data["bracelet"].texture, true)
        else
            ClearPedProp(ped, 7)
        end
    end
end)

AddEventHandler('qb-clothing:getSkin', function(cb)
    cb(skinData)
end)
```

{% endtab %}

{% tab title="illenium-appearance" %}

```lua
--Add this at the bottom of illenium-appearance/client/outfits.lua

RegisterNetEvent('qb-clothing:client:loadOutfit2', function(oData)
    local ped = cache.ped

    local data = oData.outfitData

    if typeof(data) ~= "table" then
        data = json.decode(data)
    end

    -- Pants
    if data["pants"] ~= nil then
        SetPedComponentVariation(ped, 4, data["pants"].item, data["pants"].texture, 0)
    end

    -- Arms
    if data["arms"] ~= nil then
        SetPedComponentVariation(ped, 3, data["arms"].item, data["arms"].texture, 0)
    end

    -- T-Shirt
    if data["t-shirt"] ~= nil then
        SetPedComponentVariation(ped, 8, data["t-shirt"].item, data["t-shirt"].texture, 0)
    end

    -- Vest
    if data["vest"] ~= nil then
        SetPedComponentVariation(ped, 9, data["vest"].item, data["vest"].texture, 0)
    end

    -- Torso 2
    if data["torso2"] ~= nil then
        SetPedComponentVariation(ped, 11, data["torso2"].item, data["torso2"].texture, 0)
    end

    -- Shoes
    if data["shoes"] ~= nil then
        SetPedComponentVariation(ped, 6, data["shoes"].item, data["shoes"].texture, 0)
    end

    -- Badge
    if data["decals"] ~= nil then
        SetPedComponentVariation(ped, 10, data["decals"].item, data["decals"].texture, 0)
    end

    -- Accessory
    local tracker = Config.TrackerClothingOptions

    if data["accessory"] ~= nil then
        if Framework.HasTracker() then
            SetPedComponentVariation(ped, 7, tracker.drawable, tracker.texture, 0)
        else
            SetPedComponentVariation(ped, 7, data["accessory"].item, data["accessory"].texture, 0)
        end
    else
        if Framework.HasTracker() then
            SetPedComponentVariation(ped, 7, tracker.drawable, tracker.texture, 0)
        else
            local drawableId = GetPedDrawableVariation(ped, 7)
            
            if drawableId ~= -1 then
                local textureId = GetPedTextureVariation(ped, 7)
                if drawableId == tracker.drawable and textureId == tracker.texture then
                    SetPedComponentVariation(ped, 7, -1, 0, 2)
                end
            end
        end
    end

    -- Mask
    if data["mask"] ~= nil then
        SetPedComponentVariation(ped, 1, data["mask"].item, data["mask"].texture, 0)
    end

    -- Bag
    if data["bag"] ~= nil then
        SetPedComponentVariation(ped, 5, data["bag"].item, data["bag"].texture, 0)
    end

    -- Hat
    if data["hat"] ~= nil then
        if data["hat"].item ~= -1 then
            SetPedPropIndex(ped, 0, data["hat"].item, data["hat"].texture, true)
        else
            ClearPedProp(ped, 0)
        end
    end

    -- Glass
    if data["glass"] ~= nil then
        if data["glass"].item ~= -1 then
            SetPedPropIndex(ped, 1, data["glass"].item, data["glass"].texture, true)
        else
            ClearPedProp(ped, 1)
        end
    end

    -- Ear
    if data["ear"] ~= nil then
        if data["ear"].item ~= -1 then
            SetPedPropIndex(ped, 2, data["ear"].item, data["ear"].texture, true)
        else
            ClearPedProp(ped, 2)
        end
    end

    -- Watch
    if data["watch"] ~= nil then
        if data["watch"].item ~= -1 then
            SetPedPropIndex(ped, 6, data["watch"].item, data["watch"].texture, true)
        else
            ClearPedProp(ped, 6)
        end
    end

    -- Bracelet
    if data["bracelet"] ~= nil then
        if data["bracelet"].item ~= -1 then
            SetPedPropIndex(ped, 7, data["bracelet"].item, data["bracelet"].texture, true)
        else
            ClearPedProp(ped, 7)
        end
    end
end)
```

{% endtab %}

{% tab title="ak47\_qb\_clothing" %}
No modification required.
{% endtab %}
{% endtabs %}

### **Step 4:**

* Restart the server 2 times (for item sync)

### **Notes:**&#x20;

* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.
* Renaming of the script is not allowed.
* We do not support custom frameworks, highly modified versions of ESX, or deprecated/outdated versions of anything.&#x20;


# Exports


# Client

### 💻 Core Client Operations

These functions are used to check the core state of the client-side inventory or handle primary interactions.

#### Ready

Checks if the player's inventory system has fully loaded and is ready to be used. It's recommended to check this before performing other client-side inventory operations during script initialization.

```lua
exports['ak47_inventory']:Ready()
```

Return: `boolean`

* `true` if ready, `false` otherwise

### 🗃️ UI & Inventory Management

Functions for managing the inventory user interface and its state.

#### OpenInventory

Opens the inventory user interface. It can open a specific inventory (like a stash), a dynamically created one, or another player's inventory.

```lua
exports['ak47_inventory']:OpenInventory(data)
```

* data: `table`, `string`, or `number`
  * `string:` target inventory identifier (e.g., 'stash:1234')
  * `number:` target player server ID to open their inventory
  * `table:` Configuration data for creating and opening a temporary/dynamic inventory
    * `identifier` (unique inventory identifier)
    * `label` (display label)
    * `type` (inventory type: stash, backpack, glovebox, trunk)
    * `maxWeight` (maximum weight capacity)
    * `slots` (total number of slots)

Example:

{% tabs %}
{% tab title="Existing Stash" %}

```lua
exports['ak47_inventory']:OpenInventory('stash:1234')
```

{% endtab %}

{% tab title="New/Housing Stash" %}

```lua
exports['ak47_inventory']:OpenInventory({
	identifier = 'stash:1234',
	label = 'Housing Stash',
	type = 'stash',
	maxWeight = 120000,
	slots = 50,
})
```

{% endtab %}

{% tab title="Player Inventory" %}

```lua
--player server id
exports['ak47_inventory']:OpenInventory(5)
```

{% endtab %}
{% endtabs %}

#### OpenNearbyInventory

Attempts to open the inventory of the nearest player to the client.

```lua
exports['ak47_inventory']:OpenNearbyInventory()
```

#### CloseInventory

Forces the inventory UI to close for the local player.

```lua
exports['ak47_inventory']:CloseInventory()
```

#### SetInventoryBusy

Sets the inventory's busy state, preventing or allowing the player to interact with their inventory UI. Useful when locking the inventory during an animation or progress bar.

```lua
exports['ak47_inventory']:SetInventoryBusy(busy)
```

* busy: `boolean` (true to lock, false to unlock)

Use Case:

```lua
local invBusy = LocalPlayer.state.invBusy
 
if invBusy then
    -- Do stuff when busy
else
    -- Do stuff when not busy
end
```

### 🔍 Data Retrieval & Getters

Functions for reading the client's inventory state, fetching item information, and searching for items.

#### Items

Retrieves the shared item configuration from the client. You can use this to fetch the definition of every item or a specific item's details (label, weight, etc.).

{% tabs %}
{% tab title="All Items" %}

```lua
exports['ak47_inventory']:Items()
```

{% endtab %}

{% tab title="Single Item" %}

```lua
exports['ak47_inventory']:Items('water')
```

{% endtab %}
{% endtabs %}

#### GetItem

Retrieves a consolidated object containing data about a specific item in the player's inventory.

```lua
exports['ak47_inventory']:GetItem(name, info, strict)
```

* name: `string`
  * item name
* info: `table` (optional)
  * filter by specific metadata
* strict: `boolean` (optional)
  * if true, strictly matches info properties; otherwise uses partial matching

Return: `table`

* item table with total item amount & properties

#### GetFirstItem

Finds and returns the data of the **first occurrence** of an item found in the inventory, regardless of how many stacks exist.

```lua
exports['ak47_inventory']:GetFirstItem(item)
```

* item: `string`
  * item name

Return: `table`

* first found item table

#### GetItemLabel

Returns the friendly display label of a specific item name.

```lua
exports['ak47_inventory']:GetItemLabel(name)
```

* name: `string`
  * item name

Return: `string`

* item label

#### HasItems

Verifies if the player's inventory contains a list of specific items in the required quantities.

```lua
exports['ak47_inventory']:HasItems(items)
```

* items: `table`
  * Key-value table of items: `{[item_name] = amount}`

Return: `boolean`, `table`

* hasAll (true if all items are present)
* missingItems (table of missing items and the amounts still needed)

Example:

```lua
exports['ak47_inventory']:HasItems({
    water = 5,
    bread = 3
})
```

#### Search

A versatile search function to find item counts or full slot data in the local player's inventory.

```lua
exports['ak47_inventory']:Search(searchType, item, info)
```

* searchType: `string`
  * `'slots'` (returns a table of slots where the item was found at)
  * `'amount'` (returns the amount of the specified item in the player's inventory. If searching for multiple items, returns key-value pairs of `itemName = amount`)
* item: `table` or `string`
  * Can be a single item name or an array of item names
* info: `table` or `string` (optional)
  * If info is provided as a string, it will search the item's `info.type` property

**Amount**

{% tabs %}
{% tab title="Single Item" %}

```lua
local amount = exports['ak47_inventory']:Search('amount', 'water')
print('You have ' .. amount .. ' water')
```

{% endtab %}

{% tab title="Multiple Items" %}

```lua
local inventory = exports['ak47_inventory']:Search('amount', {'meat', 'skin'}, {grade="1"})
 
if inventory then
    for name, amount in pairs(inventory) do
        print('You have ' .. amount .. ' ' .. name)
    end
end
```

{% endtab %}
{% endtabs %}

**Slots**

{% tabs %}
{% tab title="Single Item" %}

```lua
local water = exports['ak47_inventory']:Search('slots', 'water')
local amount = 0
 
for _, v in pairs(water) do
    print(v.slot .. ' contains ' .. v.amount .. ' water ' .. json.encode(v.info))
    amount = amount + v.amount
end
 
print('You have ' .. amount .. ' water')
```

{% endtab %}

{% tab title="Multiple Items" %}

```lua
local items = exports['ak47_inventory']:Search('slots', {'meat', 'skin'}, 'deer')
 
if items then
    for name, data in pairs(items) do
        local amount = 0
 
        for _, v in pairs(data) do
            if v.slot then
                print(v.slot .. ' contains ' .. v.amount .. ' ' .. name .. ' ' .. json.encode(v.info))
                amount = amount + v.amount
            end
        end
 
        print('You have ' .. amount .. ' ' .. name)
    end
end
```

{% endtab %}
{% endtabs %}

#### GetAmount

Returns the total numeric count of a specific item in the player's inventory. It sums up all stacks of that item.

```lua
exports['ak47_inventory']:GetAmount(itemName, info, strict)
```

* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)
  * Strictly match info properties, otherwise use partial matching

Return: `number`

* total amount of the item

#### GetPlayerItems

Retrieves the list (table) of items currently in the local player's inventory.

```lua
exports['ak47_inventory']:GetPlayerItems()
```

Return: `table`

* table of player items

#### GetPlayerWeight

Retrieves the current total weight of the items in the local player's inventory.

```lua
exports['ak47_inventory']:GetPlayerWeight()
```

Return: `number`

* current inventory weight

#### GetPlayerMaxWeight

Retrieves the maximum weight capacity of the local player's inventory.

```lua
exports['ak47_inventory']:GetPlayerMaxWeight()
```

Return: `number`

* maximum inventory weight

### 📍 Slot Operations

Functions for interacting with specific slots in the local player's inventory.

#### GetSlotWithItem

Retrieves the data of the **first slot found** containing the specific item.

```lua
exports['ak47_inventory']:GetSlotWithItem(itemName, info, strict)
```

* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `table` or `nil`

* slot data table, if found

#### GetSlotIdWithItem

Retrieves the slot index (ID) of the **first slot found** containing the specific item.

```lua
exports['ak47_inventory']:GetSlotIdWithItem(itemName, info, strict)
```

* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `number` or `nil`

* slot ID, if found

#### GetSlotsWithItem

Retrieves a list of **all slot data tables** that contain the specific item.

```lua
exports['ak47_inventory']:GetSlotsWithItem(itemName, info, strict)
```

* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `table` or `nil`

* array of slot data tables

#### GetSlotIdsWithItem

Retrieves a list of **all slot IDs** containing the specific item.

```lua
exports['ak47_inventory']:GetSlotIdsWithItem(itemName, info, strict)
```

* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `table` or `nil`

* array of slot IDs

#### GetSlot

Retrieves the item data stored at a specific slot index in the player's inventory. Returns `nil` if the slot is empty.

```lua
exports['ak47_inventory']:GetSlot(slotId)
```

* slotId: `number`
  * the slot index to query

Return: `table` or `nil`

* the item data table at that slot

#### UseSlot

Triggers the use action for the item currently occupying a specific slot.

```lua
exports['ak47_inventory']:UseSlot(slotId)
```

* slotId: `number`
  * the slot index to use

### ⚔️ Weapons

Functions for managing equipped weapons on the client side.

#### UnEquipeWeapon

Unequips the currently equipped weapon from the local player's hands.

```lua
exports['ak47_inventory']:UnEquipeWeapon()
```


# Server

### 📦 Core Item Operations

These functions are the most commonly used for interacting with player items, such as adding, removing, and checking item counts.

#### AddItem

Adds an item to an inventory. This function handles all logic regarding stacking, weight limits, and finding available slots. If the inventory is full or the item is invalid, it returns `false`.

```lua
exports['ak47_inventory']:AddItem(inv, item, amount, slot, info, weight, expiretime)
```

* inv: `string` or `number`
  * player id (source) or unique inventory identifier
* item: `string`
  * item name
* amount: `number`
  * amount of the item to add
* slot: `number` (optional)
  * specific slot index to place the item in (force placement)
* info: `table` (optional)
  * item metadata table (e.g., durability, serial number)
* weight: `number` (optional)
  * override the default weight of this specific item instance
* expiretime: `number` (optional)
  * override the default expiration timestamp of this specific item instance

Return: `boolean`, `string` or `number`

* success (true/false)
* reason (if failed) OR slot index (if success)

#### RemoveItem

Removes a specific quantity of an item from an inventory. It prioritizes removing from specific slots if provided, otherwise it searches and removes from the first available stack(s).

```lua
exports['ak47_inventory']:RemoveItem(inv, item, amount, slot)
```

* inv: `string` or `number`
  * player id (source) or unique inventory identifier
* item: `string`
  * item name
* amount: `number`
  * amount of the item to remove
* slot: `number` (optional)
  * specific slot index to remove the item from

Return: `boolean`, `string`

* success (true/false)
* reason (e.g., "notenough")

#### HasItems

Verifies if an inventory contains a list of specific items in the required quantities. This is commonly used for crafting recipes or trade requirements.

```lua
exports['ak47_inventory']:HasItems(inv, items)
```

* inv: `string` or `number`
* items: `table`
  * Key-value table: `{[item_name] = amount}` (e.g., `{'bread' = 1, 'water' = 2}`)

Return: `boolean`, `table`

* hasAll (true if all items are present)
* missingItems (table of missing items and the amounts still needed)

#### GetItem

Retrieves a consolidated object containing data about a specific item in an inventory. This is useful for checking if a player has an item and getting its total combined count across all slots.

```lua
exports['ak47_inventory']:GetItem(inv, item, info, strict)
```

* inv: `string` or `number`
* item: `string`
  * item name
* info: `table` (optional)
  * filter by specific metadata
* strict: `boolean` (optional)
  * if true, checks for an exact metadata match (does not sum duplicates with different metadata)

Return: `table`

* item table with total item amount & properties

#### GetAmount

Returns the total numeric count of a specific item in an inventory. It sums up all stacks of that item.

```lua
exports['ak47_inventory']:GetAmount(identifier, item, info, strict)
```

* identifier: `string` or `number`
* item: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `number`

#### CanAddItem

Checks if an inventory has enough weight capacity and slot space to accept an item. This function **does not** actually add the item, it only performs the check.

```lua
exports['ak47_inventory']:CanAddItem(identifier, item, amount, skipWeight)
```

* identifier: `string` or `number`
* item: `string`
* amount: `number`
* skipWeight: `boolean` (optional)
  * if true, ignores weight limit checks (only checks slot limit)

Return: `boolean`

#### CanRemoveItem

Checks if an inventory currently has enough of a specific item to remove the requested amount.

```lua
exports['ak47_inventory']:CanRemoveItem(identifier, item, amount)
```

* identifier: `string` or `number`
* item: `string`
* amount: `number`

Return: `boolean`

**CanCarryAmount**

Calculates the maximum amount of a specific item that fits into the inventory's remaining weight capacity.

```lua
exports['ak47_inventory']:CanCarryAmount(identifier, item)
```

* identifier: `string` or `number`
* item: `string` or `table`

Return: `number`

* amount that fits

#### CanSwapItem

Determines if two items can be swapped between slots or inventories without exceeding weight limits. This is used during drag-and-drop operations.

```lua
exports['ak47_inventory']:CanSwapItem(inv, firstItem, firstItemCount, testItem, testItemCount)
```

* inv: `string` or `number`
* firstItem: `string`
* firstItemCount: `number`
* testItem: `string`
* testItemCount: `number`

Return: `boolean`

### 🗃️ Inventory Management

Functions for managing inventory instances (opening, creating, loading, saving).

#### OpenInventory

Opens the inventory user interface for a specific player. This allows them to view the specified inventory identifier.

```lua
exports['ak47_inventory']:OpenInventory(source, identifier, data)
```

* source: `number`
  * The player ID opening the inventory
* identifier: `string`
  * The identifier of the inventory to open
* data: `table` (optional)
  * Configuration data for creating the inventory on-the-fly (e.g., dumpsters)

#### CloseInventory

Forces the inventory UI to close for a specific player.

```lua
exports['ak47_inventory']:CloseInventory(source)
```

* source: `number`

#### CreateInventory

Initializes a new inventory in the system memory. This is required for creating stashes, trunks, or other non-player inventories before they can be accessed.

```lua
exports['ak47_inventory']:CreateInventory(identifier, data)
```

* identifier: `string`
* data: `table`
  * label: `string`
  * maxWeight: `number`
  * slots: `number`
  * type: `string` (backpack, stash, player, shop, trunk, glovebox)
  * type2: `string` (optional, e.g. 'smallBackpack')
  * temp: `boolean` (optional, true = not saved to database)
  * whitelist: `table` (optional)
  * blacklist: `table` (optional)

Example:

```lua
-- server side
exports['ak47_inventory']:CreateInventory('housing:123', {
    label = 'Housing',
    maxWeight = 500000,
    slots = 50,
    type = 'stash',
})

--open from server side
exports['ak47_inventory']:OpenInventory(source, 'housing:123')

--open from client side
exports['ak47_inventory']:OpenInventory('housing:123')
```

#### LoadInventory

lLoads an inventory from the database. If provided with `data`, it can also create the inventory if it doesn't already exist.

{% tabs %}
{% tab title="Load Existing Inventory" %}

```lua
exports['ak47_inventory']:LoadInventory(identifier)
```

{% endtab %}

{% tab title="Create & Load" %}

```lua
--if missing in database then create & load an inventory
exports['ak47_inventory']:LoadInventory(identifier, data)
```

{% endtab %}
{% endtabs %}

Return: `boolean`

Example:

```lua
-- server side
exports['ak47_inventory']:LoadInventory('housing:123', {
    label = 'Housing',
    maxWeight = 500000,
    slots = 50,
    type = 'stash',
})
```

#### SaveInventory

Forces an immediate save of a specific inventory to the database.

```lua
exports['ak47_inventory']:SaveInventory(identifier)
```

* identifier: `string` or `number`

**SaveAllInventory**

Forces a save of *all* currently loaded inventories that have pending changes. This is typically run automatically on server stop/restart.

```lua
exports['ak47_inventory']:SaveAllInventory()
```

#### ClearInventory

Removes all items from an inventory, effectively wiping it clean.

```lua
exports['ak47_inventory']:ClearInventory(identifier)
```

* identifier: `string` or `number`

#### UnloadInventory

Unloads an inventory from server memory (saving it first) to free up resources. It will be reloaded from the database if accessed again.

```lua
exports['ak47_inventory']:UnloadInventory(identifier)
```

* identifier: `string` or `number`

#### DeleteInventory

Permanently deletes an inventory from the database and removes it from server memory.

```lua
exports['ak47_inventory']:DeleteInventory(identifier)
```

* identifier: `string` or `number`

### 🔍 Data Retrieval & Getters

Functions for reading specific states or configurations.

#### GetInventory

Retrieves the full inventory object, including the list of items, current weight, max weight, and other configuration properties.

```lua
exports['ak47_inventory']:GetInventory(identifier)
```

* identifier: `string` or `number`

Return: `table`

* inventoryTable

#### GetInventoryItems

Retrieves just the list (table) of items from an inventory. Useful for iteration when you don't need weight or other inventory properties.

```lua
exports['ak47_inventory']:GetInventoryItems(identifier)
```

* identifier: `string` or `number`

Return: `table`

* itemsTable

#### Search

A versatile search function to find item counts or full slot data.

```lua
exports['ak47_inventory']:Search(identifier, search, item, info)
```

* identifier: `string` or `number`
* search: `string`
  * `'slots'` (returns a table of item data) or `'count'`/`'amount'` (returns the total numeric count)
* item: `table` (list of item names) or `string`
* info: `table` or `string` (optional)

#### Items

Retrieves the shared item configuration from the server. You can use this to fetch the definition of every item or a specific item's details (label, weight, etc.).

{% tabs %}
{% tab title="All Items" %}

```lua
exports['ak47_inventory']:Items()
```

{% endtab %}

{% tab title="Single Item" %}

```lua
exports['ak47_inventory']:Items('water')
```

{% endtab %}
{% endtabs %}

#### GetItemLabel

Returns the friendly display label of a specific item name (e.g., returns "Water Bottle" for input "water").

```lua
exports['ak47_inventory']:GetItemLabel(item)
```

* item: `string`
  * item name

Return: `string`

#### GetCurrentWeapon

Retrieves the item data of the weapon currently equipped by the player.

```lua
exports['ak47_inventory']:GetCurrentWeapon(identifier)
```

* identifier: `string` or `number`

Return: `table`

* weaponItemData

#### CanCarryWeight

Checks if the inventory has enough remaining capacity to hold a specific amount of weight.

```lua
exports['ak47_inventory']:CanCarryWeight(identifier, weight)
```

* identifier: `string` or `number`
* weight: `number`

Return: `boolean`, `number`

* canHold (true/false)
* availableWeight (remaining weight capacity)

#### GetEmptySlot

Returns the index of the **first available empty slot** in the inventory. Returns `nil` if full.

```lua
exports['ak47_inventory']:GetEmptySlot(identifier)
```

* identifier: `string` or `number`

Return: `number`

* slotId

#### GetContainerFromSlot

If a slot contains a container item (like a backpack), this retrieves the actual inventory data object associated with that container.

```lua
exports['ak47_inventory']:GetContainerFromSlot(identifier, slotId)
```

* identifier: `string` or `number`
* slotId: `number`

Return: `table`

* containerInventoryData

### 📍 Slot Operations

Functions for interacting with specific slots or finding item locations.

#### GetSlot

Retrieves the item data stored at a specific slot index. Returns `nil` or an empty table if the slot is empty.

```lua
exports['ak47_inventory']:GetSlot(identifier, slot)
```

* identifier: `string` or `number`
* slot: `number`

Return: `table`

* The item data table at that slot

#### GetSlotForItem

Finds the optimal slot index for an item. It will return the index of an existing stack (if stackable and space exists) or the first available empty slot.

```lua
exports['ak47_inventory']:GetSlotForItem(identifier, itemName, info)
```

* identifier: `string` or `number`
* itemName: `string`
* info: `table` (optional)

Return: `number`

* slotId

#### GetSlotWithItem

Retrieves the data of the **first slot found** containing the specific item.

```lua
exports['ak47_inventory']:GetSlotWithItem(identifier, itemName, info, strict)
```

* identifier: `string` or `number`
* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `table`

* slotData

#### GetSlotIdWithItem

Retrieves the slot index (ID) of the **first slot found** containing the specific item.

```lua
exports['ak47_inventory']:GetSlotIdWithItem(identifier, itemName, info, strict)
```

* identifier: `string` or `number`
* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `number`

* slotId

#### GetSlotsWithItem

Retrieves a list of **all slot data tables** that contain the specific item. Useful for finding every instance of an item split across multiple slots.

```lua
exports['ak47_inventory']:GetSlotsWithItem(identifier, itemName, info, strict)
```

* identifier: `string` or `number`
* itemName: `string`
* info: `table` (optional)
* strict: `boolean` (optional)

Return: `table`

* list of slotData objects

#### GetSlotIdsWithItem

Retrieves a list of **all** slot IDs containing the item.

```lua
exports['ak47_inventory']:GetSlotIdsWithItem(inv, itemName, info, strict)
```

* **Return**: `table` (List of IDs).

#### GetItemSlots

Provides detailed information about where an item is located, including a map of slots-to-amounts and the count of empty slots.

```lua
exports['ak47_inventory']:GetItemSlots(identifier, item, info)
```

* identifier: `string` or `number`
* item: `table` (must have .name) or `string`
* info: `table` (optional)

Return: `table`, `number`, `number`

* slots (key=slotId, value=amount)
* totalAmount
* emptySlotsCount

#### GetFirstItem

Finds and returns the data of the **first occurrence** of an item found in the inventory, regardless of how many stacks exist.

```lua
exports['ak47_inventory']:GetFirstItem(inv, item)
```

* inv: `string` or `number`
* item: `string`
  * item name

Return: `table`

* first found item table

### ✏️ Updates & Modification

Functions that modify existing items or inventory properties.

#### SetItemInfo

Updates the metadata (info table) of an item in a specific slot.

```lua
exports['ak47_inventory']:SetItemInfo(identifier, slot, info)
```

* identifier: `string` or `number`
* slot: `number`
* info: `table`

#### SetQuality

Sets the quality (durability) of an item in a specific slot.

```lua
exports['ak47_inventory']:SetQuality(identifier, slot, quality)
```

* identifier: `string` or `number`
* slot: `number`
* quality: `number`

#### RemoveQuality

Reduces the quality (durability) of an item in a specific slot. Can optionally remove the item if quality reaches zero.

```lua
exports['ak47_inventory']:RemoveQuality(identifier, slot, value)
```

* identifier: `string` or `number`
* slot: `number`
* value: `number`

#### SetMaxWeight

Dynamically updates the maximum weight limit of an inventory.

```lua
exports['ak47_inventory']:SetMaxWeight(identifier, newWeight)
```

* identifier: `string` or `number`
* newWeight: `number`

#### SetSlotCount <a href="#setslotcount" id="setslotcount"></a>

Changes the number of slots an inventory is currently have.

```lua
exports['ak47_inventory']:SetSlotCount(identifier, slots)
```

* identifier: `string` or `number`
* slots: `number`

#### UpdatePlayerInv

Forces a synchronization of the server-side inventory data to the client-side UI for a specific player. Use this if you modify inventory data manually.

```lua
exports['ak47_inventory']:UpdatePlayerInv(identifier)
```

* identifier: `string` or `number`

#### SetInvItems

Completely overwrites the items in an inventory with a new table of items. **Warning**: This replaces the entire item set.

```lua
exports['ak47_inventory']:SetInvItems(identifier, items)
```

* identifier: `string` or `number`
* items: `table`

#### ClearClothing

Removes all items stored in the clothing slots of an inventory (e.g., masks, hats).

```lua
exports['ak47_inventory']:ClearClothing(identifier)
```

* identifier: `string` or `number`

### 🛠️ Administrative & Special

Specialized functions for shops, police systems, and vehicle handling.

#### ConfiscateInventory

Moves all items from a player's inventory to a temporary "confiscated" storage (prefixed with `conf:`) and clears their main inventory.

```lua
exports['ak47_inventory']:ConfiscateInventory(identifier)
```

* identifier: `string` or `number`

#### ReturnInventory

Restores items from the "confiscated" storage back to the player's main inventory.

```lua
exports['ak47_inventory']:ReturnInventory(identifier)
```

* identifier: `string` or `number`

#### CreateShop

Helper to create a shop inventory with items for sale. Supports job restrictions, licenses, and custom sell prices.

```lua
exports['ak47_inventory']:CreateShop(identifier, name, itemTable, account)
```

* **identifier**: `string` - Unique ID for this shop.
* **name**: `string` - Label displayed to the player.
* **itemTable**: `table` - List of items with configurations (see example below).
* **account**: `string` - Currency to use (e.g., 'money', 'bank', 'black\_money').

**Supported `itemTable` options:**

* `item`: Item name (required)
* `buyPrice`: Cost to purchase from shop
* `sellPrice`: Money given to player when selling TO shop
* `stock`: Available quantity (-1 for infinite)
* `jobs`: Table of allowed jobs/grades `{[jobName] = minGrade}`
* `gangs`: Table of allowed gangs/grades `{[gangName] = minGrade}`
* `license`: Table `{ name = "weapon", class = "A" }` (optional class)
* `requires`: Table of items needed to purchase `{[item_name] = amount}`
* `hasSerial`: Boolean (true generates random serial on purchase)
* `quality`: Initial quality (default 100)

**Example:**

```lua
local shopItems = {
    {
        item = 'bread',
        buyPrice = 10,
        stock = 100,
    },
    {
        item = 'weapon_pistol',
        buyPrice = 5000,
        stock = 5,
        hasSerial = true,
        jobs = { police = 0 }, -- Only police can buy
        license = { name = 'weapon' } -- Requires weapon license
    },
    {
        item = 'gold_bar',
        sellPrice = 1000, -- Shop buys gold bars for 1000
        buyPrice = 2000
    }
}

exports['ak47_inventory']:CreateShop('general_store', 'General Store', shopItems, 'money')

-- open shop from client side
exports['ak47_inventory']:OpenInventory('general_store')

-- open shop from server side
exports['ak47_inventory']:OpenInventory(source, 'general_store')
```

#### SetWhitelistedItemsForContainer

Restricts an inventory to only accept a specific list of items. Any item not in this list cannot be added.

```lua
exports['ak47_inventory']:SetWhitelistedItemsForContainer(identifier, items)
```

* identifier: `string` or `number`
* items: `table` (list of allowed item names)

Example:

```lua
exports['ak47_inventory']:SetWhitelistedItemsForContainer('stash:123', {'water', 'bread'})
```

#### SetBlacklistedItemsForContainer

Restricts an inventory by preventing specific items from being stored in it.

```lua
exports['ak47_inventory']:SetBlacklistedItemsForContainer(identifier, items)
```

* identifier: `string` or `number`
* items: `table` (list of disallowed item names)

Example:

```lua
exports['ak47_inventory']:SetBlacklistedItemsForContainer('stash:123', {'water', 'bread'})
```

#### OnChangeVehiclePlate

A utility helper to migrate trunk and glovebox inventories when a vehicle's plate is changed (e.g., applying a fake plate).

```lua
exports['ak47_inventory']:OnChangeVehiclePlate(oldPlate, newPlate)
```

* oldPlate: `string`
* newPlate: `string`


# Event Handlers


# Client

### 📦 Item Events

Events triggered on the client when their local inventory items change.

#### On Remove Item

Triggered when the local player successfully removes an item from their inventory.

```lua
RegisterNetEvent('ak47_inventory:onRemoveItem', function(item, amount, slot, has)
    -- Your code here
end)
```

**Parameters:**

* item: `string`
  * The name of the item removed
* amount: `number`
  * The quantity of the item that was removed
* slot: `number`
  * The slot index the item was removed from
* has: `number`
  * The total amount of this item the player still has after removal

#### On Add Item

Triggered when the local player successfully receives an item into their inventory.

```lua
RegisterNetEvent('ak47_inventory:onAddItem', function(item, amount, slot, has)
    -- Your code here
end)
```

**Parameters:**

* item: `string`
  * The name of the item added
* amount: `number`
  * The quantity of the item that was added
* slot: `number`
  * The slot index the item was added to
* has: `number`
  * The total amount of this item the player now has

### ⚔️ Weapon Events

Events related to weapon interactions on the client side.

#### On Equip Weapon

Triggered when the local player equips a weapon.

```lua
RegisterNetEvent('ak47_inventory:onEquipWeapon', function(currentWeapon)
    -- Your code here
end)
```

**Parameters:**

* currentWeapon: `table`
  * The item data table of the equipped weapon

#### On UnEquip Weapon

Triggered when the local player unequips their currently held weapon.

```lua
RegisterNetEvent('ak47_inventory:onUnEquipWeapon', function(currentWeapon)
    -- Your code here
end)
```

**Parameters:**

* currentWeapon: `table`
  * The item data table of the weapon that was unequipped

### 👕 Clothing Events

Events triggered when clothing items (masks, helmets, etc.) are added or removed.

#### On Add Clothing

Triggered when the local player puts on a clothing item.

```lua
RegisterNetEvent('ak47_inventory:onAddClothing', function(clothingType, skinData, toOtherInventory)
    -- Your code here
end)
```

**Parameters:**

* clothingType: `string`
  * The type of clothing equipped (e.g., `'mask'`, `'helmet'`)
* skinData: `table`
  * The metadata associated with the clothing item
* toOtherInventory: `boolean`
  * `true` if this clothing item was transferred from another inventory

#### On Remove Clothing

Triggered when the local player takes off a clothing item.

```lua
RegisterNetEvent('ak47_inventory:onRemoveClothing', function(clothingType, toOtherInventory)
    -- Your code here
end)
```

**Parameters:**

* clothingType: `string`
  * The type of clothing unequipped (e.g., `'mask'`, `'helmet'`)
* toOtherInventory: `boolean`
  * `true` if this clothing item was transferred to another inventory


# Server

### 📦 Item Events

Events triggered when items are manipulated in a player's inventory on the server side.

#### On Remove Item

Triggered on the server when a player successfully removes an item from their inventory.

```lua
AddEventHandler('ak47_inventory:onRemoveItem', function(source, item, amount, slot, has)
    -- Your code here
end)
```

**Parameters:**

* source: `number`
  * The server ID of the player
* item: `string`
  * The name of the item removed
* amount: `number`
  * The quantity of the item that was removed
* slot: `number`
  * The slot index the item was removed from
* has: `number`
  * The total amount of this item the player still has after removal

#### On Add Item

Triggered on the server when a player successfully receives an item into their inventory.

```lua
AddEventHandler('ak47_inventory:onAddItem', function(source, item, amount, slot, has)
    -- Your code here
end)
```

**Parameters:**

* source: `number`
  * The server ID of the player
* item: `string`
  * The name of the item added
* amount: `number`
  * The quantity of the item that was added
* slot: `number`
  * The slot index the item was added to
* has: `number`
  * The total amount of this item the player now has


# Commands

### Player Commands

* `/search` (Police only - configured in config.lua)
* `/rob` (Configured in config.lua)

### Admin Commands

* `/openinv [player id or identifier]` - Open someone else's inventory or stash
* `/clearinv [player id]` - Wipe a player's inventory
* `/saveinv [player id]` - Force save a specific player's inventory to database
* `/saveinvs` - Force save all loaded inventories to database
* `/giveitem [player id] [item name] [amount]` - Give an item directly to a player
* `/clearclothing [player id]` - Clear clothing items from a player
* `/clothingimg` - Utility command for clothing images

### Player Key Bindings

The inventory relies on FiveM's native `RegisterKeyMapping` functionality. This means players can customize their own keys without server owner intervention by navigating to **Esc > Settings > Key Bindings > FiveM** in their GTA V client.

By default, the following actions are bindable:

* **Open Inventory** (`inv` command)
* **Show Hotbar** (`+hotbar` command)
* **Use Hotkey 1-5** (`+hotkey1` through `+hotkey5` commands)
* **Reload Weapon** (`+wreload` command)
* **Throw Weapon** (`wthrow` command)
* **Scroll Up/Down** (`+scroll` / `-scroll` commands for mouse wheel integration)


# Guides


# Migration

If you are replacing an existing inventory system (specifically **OX Inventory** or **Quasar (qs) Inventory**), `ak47_inventory` includes powerful built-in migration tools to seamlessly transfer your server's data.

#### Player Data Migration (Automatic)

When you first start `ak47_inventory`, the `server/_init.lua` script will attempt to detect if you are running an existing `ox_inventory`, `esx`, or `qbcore` inventory database structure.

* **OX Inventory:** It will automatically read the `ox_inventory` database tables and migrate players, vehicles, and stashes into the `ak47_inventory` format.
* **ESX/QBCore Native:** It will decode the `users` (or `players`) table's JSON inventory data and reconstruct it securely inside `ak47_inventory`.

> \[!WARNING] After the automatic database migration completes, you will see a message instructing you to restart your server. You **must** restart the server to finalize the data loading.

#### Item & Config Migration (`convert/converter.lua`)

If you have heavily customized items and weapons in OX or Quasar, you do not need to manually rewrite them into `ak47_inventory` format. The `convert/` folder handles this.

1. Locate the `convert/ox/` or `convert/qs/` folders.
2. Place your existing `items.lua` or `weapons.lua` from your old inventory into the corresponding folder.
3. Start the `ak47_inventory` script.
4. The system will detect the old item lists and execute `convert/converter.lua`.
5. Your old items, weights, and durability logic will be automatically rewritten into `ak47_inventory` format and saved into the `shared/` directory.

> \[!TIP] Just like with the database migration, if the converter successfully converts items, you will see a console prompt telling you to restart the inventory to load the new converted items.


# Configuration

The `ak47_inventory` resource is highly configurable through the files located in the `configs/` directory. This guide explains the purpose of each configuration file and key settings you can adjust.

### `config.lua`

This is the core configuration file for the inventory. It contains general settings, shared item definitions, and framework settings.

#### Key Settings

* **Framework & SQL**: Automatically detects ESX or QBCore. You can explicitly set it if needed.
* **Player Inventory Limits**:
  * `Config.PlayerInvWeight`: Default max weight (e.g., 120000).
  * `Config.PlayerInvSlots`: Default max slots (e.g., 50).
* **Default Stash Size**:
  * `Config.DefaultStashWeight` and `Config.DefaultStashSlots` define the capacity for newly created stashes.
* **Shared Items List**: The `Config.Shared.Items` table defines every item in the game, including their labels, weights, stack sizes, and types (e.g., 'weapon', 'item', 'ammo').
* **Drop Settings**: Configure how long items stay on the ground (`Config.ClearDropInterval`) and the max capacity of a single drop pile.

### `config-weapon.lua`

This file controls all weapon-related mechanics in the inventory.

#### Key Settings

* **Spawn With Ammo**: Define how much ammo a weapon gets when it is spawned or purchased.
* **Weapon Attachments**: The `Config.Shared.Components` table maps attachment items to their respective weapon hashes.
* **Repair Mechanics**:
  * `Config.RepairWeaponWithItem` defines which items are required to repair specific weapons (e.g., repairing an AP Pistol might require 5x scrap metal and 1x weapon repair kit).

### `config-clothing.lua`

This file governs how clothing items (like masks, helmets, or glasses) function as physical items in the inventory.

#### Key Settings

* **Clothing Types**: Defines which items count as clothing.
* **Skin Data Mapping**: Ensures that when a player equips a clothing item, their character model's drawable/texture IDs are updated correctly.

### `config-backpack.lua`

This file handles the backpack system, allowing players to carry additional storage.

#### Key Settings

* **Backpack Items**: Defines which items function as backpacks.
* **Capacity**: Sets the `slots` and `maxWeight` provided by each specific backpack item.
* **Blacklists/Whitelists**: You can prevent certain items from being put into backpacks (e.g., preventing a player from putting a backpack inside another backpack).

### `config-vehicle.lua`

This file manages trunk and glovebox capacities for vehicles.

#### Key Settings

* **Class-Based Capacity**: Defines default slots and max weight based on the vehicle's class (e.g., Compacts, SUVs, Vans, Commercial).
* **Model-Based Override**: Allows you to override the capacity for specific vehicle spawn codes (e.g., giving a specific custom truck a massive trunk).

### `shared/` Directory Configurations

Unlike traditional single-file configurations, `ak47_inventory` uses a modular `shared/` directory to define all game items and objects. This ensures clean, organized data management.

* **`items.lua`**: Defines standard inventory items, including labels, weight, type, and decay/durability properties.
* **`weapons.lua`**: Defines weapon items, their associated ammo types (`ammoname`), durability degradation rates, and whether they are throwable.
* **`ammo.lua`**: Defines all ammo types (e.g., `ammo-9`, `ammo-rifle`) and their weights.
* **`components.lua`**: Defines weapon attachments (suppressors, scopes) and maps them to client component hashes.
* **`shops.lua` / `vending.lua`**: Configures shop locations, available stock, prices, required jobs/licenses, and currency types.
* **`crafting.lua`**: Defines crafting benches, their recipes, required ingredients, and crafting duration.
* **`stash.lua` / `dumpsters.lua`**: Pre-configures static map stashes and dumpster interactions.

### `webhooks.lua`

Found in the root directory, this file handles Discord integration. You can supply your Discord Webhook URLs here to automatically log vital inventory events to your server's Discord:

* **`additem` & `removeitem`**: Logs when items are spawned or deleted.
* **`swapitem`**: Logs when items are moved between slots.
* **`transfer`**: Logs when items are moved from one inventory to another (e.g., player to stash, player to player).

### `locales/` Directory

The resource is fully translatable. Inside the `locales/` folder, you will find language files (e.g., `en.lua`). You can modify all UI text, notification strings, and prompt labels here. Ensure your `config.lua` is set to point to your desired locale file.


# Custom Stash

```lua
--client side export. It will register the inventory if missing then opens
exports['ak47_inventory']:OpenInventory({
	identifier = 'stash:1234', 	--unique identifier
	label = 'Housing Stash', 	--any label
	type = 'stash',			--type stash
	maxWeight = 120000,		--max weight of the inventory
	slots = 50,			--max slot of the inventory
})

--if the stash is already registered
exports['ak47_inventory']:OpenInventory('stash:1234')
```


# External Shop

{% tabs %}
{% tab title="Server Side" %}

```lua
-- create a shop server side
local items = {
    {item = "water", buyPrice = 10, sellPrice = 5, stock = 10}, -- buy, sell & stock enabled
    {item = "water", buyPrice = 10, sellPrice = 5}, -- buy, sell enabled
    {item = "water", buyPrice = 10}, -- buy enabled
    {item = "water", sellPrice = 10}, -- sell enabled
}
exports['ak47_inventory']:CreateShop('shopUniqueId', 'Shop Name', items, 'cash')
```

{% endtab %}

{% tab title="Client Side" %}

```lua
--access the creared shop from client side
exports['ak47_inventory']:OpenInventory('shopUniqueId')
```

{% endtab %}
{% endtabs %}


# Special Item

### Protected Item

Behavior:

* No one can rob this item
* Weapon with protected attachment can't be robbed.
* If enabled in config, police can take this item.
* Player can drop or give this item.
* Can be removed by external script with `RemoveItem` function.
* Will be removed with /clearinv command.

```lua
exports['ak47_inventory']:AddItem(source, 'weapon_pistol', 1, nil, {protected = 1})
```

### Locked Item

Behavior:

* Item can't be moved to any other inventory
* Player can't drop or give this item
* Weapon with locked attachment can't be robbed/moved/droped.
* Can be removed by external script with `RemoveItem` function.
* Will be removed with /clearinv command.

```lua
exports['ak47_inventory']:AddItem(source, 'weapon_pistol', 1, nil, {locked = 1})
```


# Convert Items

### Convert ox\_inventory items

1. Copy items.lua & weapons.lua from ox\_inventory/data and place into ak47\_inventory/convert/ox/
2. Restart ak47\_inventory (it will copy all items)
3. Restart ak47\_inventory again


# Show Info Value

### Path: configs/config.lua

This variables will be visible in item Tooltip

<div align="left"><figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FVjHSHQ01IVCpMwweZ5fZ%2Fimage.png?alt=media&amp;token=f78182db-07a7-49d0-887f-3bbb45ec22fd" alt=""><figcaption></figcaption></figure></div>


# Compatibility & Aliases

The `ak47_inventory` resource contains extensive built-in compatibility bridges for both **OX Inventory** (`ox_inventory`) and **QB Inventory** (`qb-inventory` / `qb-core`).

This means that many third-party scripts designed to communicate with `ox_inventory` or `qb-inventory` via exports will often work out-of-the-box by simply targeting the equivalent `ak47_inventory` export, or because the inventory bridges their expected methods.

Below is a complete list of all alias methods exposed by `ak47_inventory` to ensure drop-in compatibility.

### OX Inventory Aliases

These exports mimic OX behavior on both the Client and Server:

**Client Exports:** `openInventory`, `openNearbyInventory`, `closeInventory`, `Items`, `useItem`, `useSlot`, `setStashTarget`, `getCurrentWeapon`, `displayMetadata`, `giveItemToTarget`, `weaponWheel`, `Search`, `GetItemCount`, `GetPlayerItems`, `GetPlayerWeight`, `GetPlayerMaxWeight`, `GetSlotWithItem`, `GetSlotIdWithItem`, `GetSlotsIdWithItem`, `GetSlotsWithItem`, `ItemList`, `notify`, `suppressItemNotifications`, `Keyboard`, `Progress`, `CancelProgress`, `ProgressActive`

**Server Exports:** `setPlayerInventory`, `UpdateVehicle`, `ConvertItems`, `Inventory`, `GetContainerFromSlot`, `SetSlotCount`, `GetInventory`, `GetInventoryItems`, `ConfiscateInventory`, `ReturnInventory`, `ClearInventory`, `Search`, `RegisterStash`, `CreateTemporaryStash`, `CustomDrop`, `CreateDropFromPlayer`, `GetCurrentWeapon`, `SetDurability` (Maps to `SetQuality`), `SetMetadata` (Maps to `SetItemInfo`), `RegisterShop`, `ItemList`, `addCash`, `removeCash`, `getCash`, `getCards`, `giveCard`, `getBank`, `RemoveInventory`, `SwapSlots`, `SetItem`, `setContainerProperties`, `registerHook`, `removeHooks`, `AddItem`, `RemoveItem`, `CanSwapItem`, `CanCarryItem`, `CanCarryWeight`, `CanCarryAmount`, `GetItem`, `GetItemSlots`, `GetSlot`, `GetSlotForItem`, `GetEmptySlot`

### QB Inventory Aliases

These exports map directly to mimic QB-Core's default inventory behavior:

**Client Exports:** `HasItem`

**Server Exports:** `SaveOfflineInventory`, `GetTotalWeight`, `GetSlotsByItem`, `GetFirstSlotByItem`, `LoadInventory`, `HasItem`, `UseItem`, `ClearInventory`, `CloseInventory`, `OpenInventory`, `OpenInventoryById`, `CreateShop`, `OpenShop`, `CanAddItem`, `AddItem`, `RemoveItem`, `SetInventory`, `SetItemData`, `GetItemBySlot`, `GetItemByName`, `GetItemsByName`, `GetItemCount`, `getItemCountByName`, `ClearInventoryByName`, `GetInventory`

> **Note:** If you are migrating a script that used `exports['ox_inventory']:SetDurability(...)`, you can simply change the resource name to `exports['ak47_inventory']:SetDurability(...)` and the inventory will seamlessly handle the translation.


# Templates


# Item

```lua
["water"] = {
    name = "water",
    label = "Water",
    weight = 1000,
    type = "item",
    durability = 1.0,   -- 1 hour
    decay = true,       -- will be removed if item quality reach to 0
    close = true,       -- close inventory on use item
    limit = 100,        -- total limit (only applied to player inventory)
    stacksize = 10,     -- limit for each slot

    client = {
        onUse = function(item)
            --this code will execute on item use

        end,
        onAdd = function(item)
            --this code will execute when you reveive this item

        end,
        onRemove = function(item)
            --this code will execute when item removed from your inventory

        end,
        TriggerEvent = 'Your Trigger Name Here',
        TriggerServerEvent = 'Your Server Trigger Name Here',
    },
    server = {
        onUse = function(source, item)
            --this code will execute on item use

        end,
        onAdd = function(source, slot)
            --this code will execute when you reveive this item

        end,
        onRemove = function(source, slot)
            --this code will execute when item removed from your inventory
            
        end,
        TriggerEvent = 'Trigger Name Here',
        TriggerClientEvent = 'Your Client Trigger Name Here',
    },

    consume = {
        attachment = { -- optional
            prop = 'prop_ld_flow_bottle',
            bone = 18905,
            position = vector3(0.12, 0.008, 0.03),
            rotation = vector3(240.0, -60.0, 0.0)
        },
        --[[
        attachment2 = { -- optional
            prop = 'prop_ld_flow_bottle',
            bone = 18905,
            position = vector3(0.12, 0.008, 0.03),
            rotation = vector3(240.0, -60.0, 0.0)
        },
        ]]
        animation = { -- optional
            dict = 'mp_player_intdrink',
            anim = 'loop_bottle',
            blendIn = 2.0,
            blendOut = 2.0,
            flag = 49,
        },
        progress = { -- optional
            label = 'Drinking Water',
            disable = {
                car = false,
                combat = false,
                move = false,
            }
        },
        status = { -- optional
            hunger = 50, --negative value supported (-50)
            thirst = 50, --negative value supported (-50)
            stress = 25, --negative value supported (-25)
        },

        delay = 5000,
        remove = 0.4, -- set 1 to remove 1 item after consuming. If you set 0.4 then it will remove 40% item quality
    }
},
```


# Weapon

```lua
['WEAPON_APPISTOL'] = {
	label = 'AP Pistol',
	weight = 1400,
	durability = 0.1, -- remove quality per bullet
	ammoname = 'ammo-9',
},
```


# Crafting

```lua
{
    name = 'Demo Crafting', -- unique name
    blip = {enable = true, id = 643, color = 35, scale = 0.6},

    --job check to open the bench
    jobs = {
        police = 0, --job name and minumum rank
        sheriff = 0
    },

    --gang check to open the bench
    gangs = {
        balles = 1, --gang name and minumum rank
        lostmc = 0,
    },

    recipes = {
        {
            item = 'weapon_pistol', -- reward item name
            amount = 1, -- reward amount
            required = {
                weapon_pistol = {amount = 1, remove = true}, -- item that will remove
                cleaner = {amount = 1}, -- item that will not remove
                toolkit = {amount = 0.2}, -- remove required item quality 20%
            },
            duration = 5,--seconds
        },
    },
    zones = {
        {
            coords = vector4(607.55, -3092.41, 6.02, 360.0),
            size = vector3(2.5, 1.0, 1.5),
        },
    },
    target = {label = 'Weapon Repair', icon = 'fa-pen-ruler', distance = 2.5},
    peds = { -- if empty then there will be no ped. Only polyzone with target
        'mp_m_waremech_01',
    }
},
```


# Stash

```lua
{
    name = 'Demo Stash', -- unique name
    jobs = {
        police = 1, --job name and minimum rank
        sheriff = 1, --job name and minimum rank
    },

    --job check to open the bench
    jobs = {
        police = 0, --job name and minumum rank
        sheriff = 0
    },

    --gang check to open the shop
    gangs = {
        balles = 1, --gang name and minumum rank
        lostmc = 0,
    },

    private = true, -- is this a private stash
    maxWeight = 50000,
    slots = 50,
    
    --either use whitelist or blacklist. don't use both
    whitelist = {'water', 'bread'},
    blacklist = {'water', 'bread'},

    zones = {
        {
            coords = vector4(445.09, -975.05, 30.67, 0.0),
            size = vector3(2.5, 1.0, 1.5),
        },
    },
    target = {label = 'Demo Stash', icon = 'fa-vault', distance = 2.5},
    peds = { -- if empty then there will be no ped. Only polyzone with target
        'mp_m_waremech_01',
    },
    scenario = 'WORLD_HUMAN_COP_IDLES' -- if nil then ped will not play any scenario
},
```


# Shop

```lua
{
    name = "Demo Shop",
    blip = {enable = false, id = 52, color = 0, scale = 0.8},
    account = 'cash', -- 'cash', 'black_money', 'bank'
    
    --job check to open the shop
    jobs = {
        police = 0, --job name and minumum rank
        sheriff = 0
    },

    --gang check to open the shop
    gangs = {
        ballas = 0, --job name and minumum rank
        sheriff = 0
    },

    items = {
        --only buy
        {item = "bread", buyPrice = 10},
        
        --only sell
        {item = "bread", sellPrice = 20},
        
        --buy & sell
        {item = "bread", buyPrice = 10, sellPrice = 20},    
        
        --buy, sell & stock enabled
        {item = "bread", buyPrice = 10, sellPrice = 20, stock = 50}, 
        
        --buy, sell, stock enabled & hasSerial
        {item = "weapon_pistol", buyPrice = 0, stock = 50, hasSerial = true}, 
        
        --buy, sell, stock enabled, hasSerial & job check while purchasing
        {item = "weapon_stungun", buyPrice = 0, stock = 50, hasSerial = true, jobs = {police = 0, sheriff = 1}}, 
            
        --buy, sell, stock enabled, hasSerial & gang check while purchasing
        {item = "weapon_stungun", buyPrice = 0, stock = 50, hasSerial = true, gangs = {police = 0, sheriff = 1}}, 
    
        -- license config for esx_license
        {item = "weapon_pistol", buyPrice = 2500, license = {name = 'weapon'}},

        -- license config for ak47_idcardv2
        {item = "weapon_pistol", buyPrice = 2500, license = {name = 'weapon', label = "WEAPON LICENSE", class = 'Pistol'}},

        -- required items enabled. note: it will not remove required items. if you need that, use crafting
        {item = "weapon_pistol", buyPrice = 2500, requires = {water = 1, bread = 1}},
    },
    zones = {
        {
            coords = vector4(45.68, -1749.04, 29.61, 53.13), -- shop & ped position
            size = vector3(2.0, 2.0, 2.0), -- zone size
        },
        {
            coords = vector4(2747.71, 3472.85, 55.67, 255.08),
            size = vector3(2.0, 2.0, 2.0),
        },
        {
            coords = vector4(-421.83, 6136.13, 31.88, 228.2),
            size = vector3(2.0, 2.0, 2.0),
        },
    },
    target = {label = "Open Shop", icon = 'fa-shop', distance = 2.0},
    peds = { --if empty then there will be no ped. Only polyzone
        'mp_m_waremech_01',
        'mp_m_waremech_01',
    },
    scenario = 'WORLD_HUMAN_COP_IDLES' -- if nil then ped will not play any scenario
},
```


# Vending

```lua
{
    name = "Deliciously Infectious", -- unique name
    model = `prop_vend_soda_01`, --prop model
    account = 'cash', --cash, bank, black_money
    target = {label = 'Deliciously Infectious', icon = 'fa-shop', distance = 2.0},
    items = {
        {item = "water", buyPrice = 10, sellPrice = 5, stock = 10}, -- buy, sell & stock enabled
        {item = "water", buyPrice = 10, sellPrice = 5}, -- buy, sell enabled
        {item = "water", buyPrice = 10}, -- buy enabled
        {item = "water", sellPrice = 10}, -- sell enabled
    },
},
```


# Dumpster

```lua
{
	item = 'water', --item name
	quality = {minimum = 50, maximum = 90}, --minimum & maximam quality of that item
	amount = {minimum = 1, maximum = 2}, --minimum & maximum amount of that item
    	chance = 90, --90% chance
},
```


# Tooltip

### Show value:

This variables will be visible in item Tooltip

```lua
Config.ShowValueFromItemInfo = {
    plate = true,
    owner = true,
    ammo = true,
    quality = true,
    serial = true,
}
```

<div align="left"><figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FZZcUozNkrHrQyvAt4ELr%2Fimage.png?alt=media&amp;token=8bc341ac-d888-4cdc-9567-bf1bd32e118e" alt=""><figcaption></figcaption></figure></div>

### Replace tooltip text:

For example, you have a key in item info named `black_money` and you want to relace this text in tooltip and show as `Black Money` you can do this by adding a translation in locales.

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2Ff60xWulkmkuiUcUW7HE7%2Fimage.png?alt=media&amp;token=e96ec2a0-9fbb-478e-ad7e-8f0da1dc1175" alt=""><figcaption></figcaption></figure>


# Fuel Script


# LegacyFuel

This script is modified to work with this inventory

### ESX:

Download and use this if you are using ESX framework.

{% file src="/files/iDPoOr8LuakFaw3D0Wup" %}

### QBCore:

Download and use this if you are using QBCore framework.

{% file src="/files/0XgqSv8ZquM5aBTKWrEs" %}


# ak47\_supermarket

{% embed url="<https://youtu.be/4ZXqnkbEh-0>" %}

### Features:

#### 🏪 Core Shop Management

* Dynamic Shop Ownership: Players can purchase, own, and manage physical storefronts across the map.
* Infrastructure Upgrades: A tiered progression system allowing owners to invest shop funds to increase stock capacity and delivery vehicle limits.
* Customizable Shop Settings: Owners can toggle specific features such as infinite stock (for server-run shops), auto-checkout, and active delivery locations.
* Physical Item Displays: A slot management system that allows owners to define exactly which items are displayed at specific physical locations inside the store.

#### 🛒 Advanced Point of Sale (POS) & Checkout

* Interactive POS Terminal: A fully featured interface for cashiers to process transactions, calculate cash change, and simulate card insertions.
* Barcode Scanner UI: An immersive checkout phase where cashiers or customers drag and drop items over a virtual scanner.
* Flexible Billing: Employees can manually construct custom bills for customers from the available shop inventory.
* Customer Shopping Carts: Players can browse the physical store, add items to a local cart, and view their total bill before checking out.

#### 📦 Logistics & Inventory Control

* Immersive Supply Deliveries: Physical shipment missions requiring players to drive delivery vehicles, unload cargo using trolleys or boxes, and track progress.
* Dynamic Pricing System: Shop owners can adjust retail prices for their products within administratively defined minimum and maximum limits.
* Real-time Stock Management: Interfaces for quick stock adjustments, adding or removing catalog products, and ordering new shipments based on vehicle capacity.

#### 🚶 AI Customer Integration (NPCs)

* Living Storefronts: AI customers physically spawn, navigate into the shop, and browse items based on custom pathing.
* Passive Income Generation: NPCs purchase items directly from the stock, generating revenue and issuing bills to the shop's account automatically.
* Queue Management: Configurable waiting lines for NPCs to simulate a realistic retail environment.

#### 👥 Employee & Financial Operations

* Staff Management: Owners can hire and fire employees, assign specific access permissions (e.g., manager vs. cashier), and set custom salaries.
* Automated Payroll: The server handles automatic paycheck disbursements to employees on a configured interval.
* Corporate Accounts: Dedicated shop bank accounts where owners can view balances, deposit personal funds, or withdraw profits.

#### 🛠️ Administrative & Builder Tools

* In-Game 3D Shop Builder: A comprehensive tool with a freecam and raycasting for admins to physically draw shop zones, place signs, map NPC paths, and designate garage areas.
* Global Item Database: An admin panel to categorize items, set universal minimum/maximum prices, and control server-wide economy constraints.
* Integrated Door Locks: A raycast-driven UI to easily select door models and lock them to specific shop permissions.

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* PolyZone: <https://github.com/mkafrin/PolyZone/releases/latest>
* ox\_target/qb-target/qtarget

### How To Install:

1. Download `ak47_supermarket` from your cfx portal.
2. Add the script in your resources folder.
3. Import `database.sql` in your server database.
4. Start the script in your `server.cfg` below framework files.
5. Make sure you have all dependencies.
6. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/7315664>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# Exports

If player is near a shop point, this export will open the closest shop.

```lua
exports['ak47_supermarket']:OpenClosestShop()
```


# ak47\_sitchair

{% embed url="<https://youtu.be/LHt1WX4gGO0>" %}

### Features:

* Sit on existing props like benches, chairs, and couches.
* Admins can create new sitting spots anywhere in-game.
* Includes a "Fly Cam" tool for precise chair placement.
* Custom chairs are saved to the database and survive restarts.
* Supports ESX, QBCore, and QBX via `ak47_lib`.
* Uses third-eye interaction for sitting and management.
* Select from multiple sitting poses via a context menu.
* Automatic offset adjustments for different animations.
* Prevents multiple players from using the same seat.
* Frees up seats if a player disconnects or dies.
* Fully translatable via locale files.

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* \[ak47\_target]\(<https://github.com/MenanAk47/ak47_target/releases/latest>)/ox\_target/qb-target/qtarget

### How To Install:

1. Download `ak47_sitchair` from your cfx portal.
2. Add the script in your resources folder.
3. Start the script in your server.cfg below framework files.
4. Make sure you have all dependencies.
5. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/6061781>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# ak47\_crafting

{% embed url="<https://youtu.be/8dVlP7ro1uc>" %}

### Features:

* Includes Lockpicking, Rapid Click, Rhythm, Sequence, and Timing Bar mechanics.
* Players earn XP to level up and unlock advanced recipes.
* Built with React and Vite for a responsive and smooth user experience.
* Easily adjust minigame difficulty, recipe ingredients, crafting times, and rewards via `config.lua`.
* Includes localization support for easy translation.
* Server-side validation ensures secure transactions and prevents exploits.
* Displays ingredients, success chances, and required skill levels for each item.

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* [ak47\_target](https://github.com/MenanAk47/ak47_target/releases/latest)/ox\_target/qb-target/qtarget

### How To Install:

1. Download `ak47_crafting` from your cfx portal.
2. Add the script in your resources folder.
3. Start the script in your server.cfg below framework files.
4. Make sure you have all dependencies.
5. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/6898590>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# config

### Pre-configured for qb/qbx

```lua
Config = {}
Config.Locale = 'en'
Config.Debug = false

Config.Locations = {
    {
        tableId = 'location1',
        position = vector4(607.08, -3087.94, 6.02, 360.0),
        size = vector3(2.5, 1.0, 1.5),
        blip = {enable = true, name = 'Crafting', sprite = 643, size = 0.6, color = 35},
    },
    {
        tableId = 'location2',
        position = vector4(607.61, -3092.66, 6.07, 270.0),
        size = vector3(2.5, 1.0, 1.5),
        blip = {enable = true, name = 'Crafting', sprite = 643, size = 0.6, color = 35},
    },
}

-- minigame: 'timingBar', 'sequence', 'rapidClick', 'rhythm', 'lockpicking'
-- difficulty: 'easy', 'medium', 'hard', 'expert'
Config.CraftingTables = {
    ['location1'] = {
        {
            requiredLevel = 0, addXp = 10, loseXp = 3,
            --minigame = 'rhythm', difficulty = 'medium',
            craftTime = 10,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 22, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 2, removeChance = 100 },
                { item = 'aluminum', name = 'Aluminum', quantity = 28, removeChance = 100  },
                { item = 'electronickit', name = 'Electronic Kit', quantity = 2, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 40, removeChance = 100 },
            },
            resultItem = { item = 'lockpick', name = 'Lockpick', description = 'Used to unlock things that you should not.', quantity = 1 }
        },
        {
            requiredLevel = 1, addXp = 20, loseXp = 5,
            minigame = 'sequence', difficulty = 'easy',
            craftTime = 10,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 30, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 42, removeChance = 100 },
            },
            resultItem = { item = 'screwdriverset', name = 'Screwdriver Set', description = 'A set of screwdrivers for various tasks.', quantity = 1 }
        },
        {
            requiredLevel = 1, addXp = 30, loseXp = 8,
            minigame = 'sequence', difficulty = 'easy',
            craftTime = 10,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 30, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 45, removeChance = 100 },
                { item = 'aluminum', name = 'Aluminum', quantity = 28, removeChance = 100 },
            },
            resultItem = { item = 'electronickit', name = 'Electronic Kit', description = 'A kit full of essential electronic components.', quantity = 1 }
        },
        {
            requiredLevel = 1, addXp = 40, loseXp = 10,
            minigame = 'sequence', difficulty = 'medium',
            craftTime = 15,
            ingredients = {
                { item = 'electronickit', name = 'Electronic Kit', quantity = 2, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 52, removeChance = 100  },
                { item = 'steel', name = 'Steel', quantity = 40, removeChance = 100  },
            },
            resultItem = { item = 'radioscanner', name = 'Radio Scanner', description = 'Allows you to listen in on radio frequencies.', quantity = 1 }
        },
        {
            requiredLevel = 3, addXp = 50, loseXp = 15,
            minigame = 'lockpicking', difficulty = 'hard',
            craftTime = 15,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 10, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 50, removeChance = 100 },
                { item = 'aluminum', name = 'Aluminum', quantity = 30, removeChance = 100 },
                { item = 'iron', name = 'Iron', quantity = 17, removeChance = 100 },
                { item = 'electronickit', name = 'Electronic Kit', quantity = 2, removeChance = 100 },
            },
            resultItem = { item = 'gatecrack', name = 'Gate Crack', description = 'A device to bypass electronic gate locks.', quantity = 1 }
        },
        {
            requiredLevel = 4, addXp = 60, loseXp = 20,
            minigame = 'rapidClick', difficulty = 'medium',
            craftTime = 15,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 36, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 24, removeChance = 100 },
                { item = 'aluminum', name = 'Aluminum', quantity = 28, removeChance = 100 },
            },
            resultItem = { item = 'handcuffs', name = 'Handcuffs', description = 'For restraining individuals. The key is optional.', quantity = 1 }
        },
        {
            requiredLevel = 5, addXp = 70, loseXp = 25,
            minigame = 'sequence', difficulty = 'medium',
            craftTime = 20,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 32, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 43, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 61, removeChance = 100 },
            },
            resultItem = { item = 'repairkit', name = 'Repair Kit', description = 'A kit for basic repairs on vehicles and other items.', quantity = 1 }
        },
        {
            requiredLevel = 6, addXp = 80, loseXp = 30,
            minigame = 'rapidClick', difficulty = 'hard',
            craftTime = 20,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 50, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 37, removeChance = 100 },
                { item = 'copper', name = 'Copper', quantity = 26, removeChance = 100 },
            },
            resultItem = { item = 'pistol_ammo', name = 'Pistol Ammo', description = 'Standard ammunition for most handguns.', quantity = 1 }
        },
        {
            requiredLevel = 7, addXp = 90, loseXp = 30,
            minigame = 'sequence', difficulty = 'easy',
            craftTime = 20,
            ingredients = {
                { item = 'iron', name = 'Iron', quantity = 60, removeChance = 100 },
                { item = 'glass', name = 'Glass', quantity = 30, removeChance = 100 },
            },
            resultItem = { item = 'ironoxide', name = 'Iron Oxide', description = 'A chemical compound, useful in certain reactions.', quantity = 1 }
        },
        {
            requiredLevel = 7, addXp = 100, loseXp = 30,
            minigame = 'sequence', difficulty = 'easy',
            craftTime = 20,
            ingredients = {
                { item = 'aluminum', name = 'Aluminum', quantity = 60, removeChance = 100 },
                { item = 'glass', name = 'Glass', quantity = 30, removeChance = 100 },
            },
            resultItem = { item = 'aluminumoxide', name = 'Aluminum Oxide', description = 'A chemical compound, often used as an abrasive.', quantity = 1 }
        },
        {
            requiredLevel = 8, addXp = 110, loseXp = 30,
            minigame = 'sequence', difficulty = 'hard',
            craftTime = 25,
            ingredients = {
                { item = 'iron', name = 'Iron', quantity = 33, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 44, removeChance = 100 },
                { item = 'plastic', name = 'Plastic', quantity = 55, removeChance = 100 },
                { item = 'aluminum', name = 'Aluminum', quantity = 22, removeChance = 100 },
            },
            resultItem = { item = 'armor', name = 'Armor', description = 'Provides a basic layer of protection against damage.', quantity = 1 }
        },
        {
            requiredLevel = 36, addXp = 120, loseXp = 30,
            minigame = 'sequence', difficulty = 'hard',
            craftTime = 25,
            ingredients = {
                { item = 'iron', name = 'Iron', quantity = 50, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 50, removeChance = 100 },
                { item = 'screwdriverset', name = 'Screwdriver Set', quantity = 3, removeChance = 100 },
                { item = 'advancedlockpick', name = 'Advanced Lockpick', quantity = 2, removeChance = 100 },
            },
            resultItem = { item = 'drill', name = 'Drill', description = 'A heavy-duty drill for breaching tough surfaces.', quantity = 1 }
        },
    },

    ['location2'] = {
        -- Attachment Bench Recipes
        {
            requiredLevel = 1, addXp = 100, loseXp = 30,
            minigame = 'sequence', difficulty = 'hard',
            craftTime = 25,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 140, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 250, removeChance = 100 },
                { item = 'rubber', name = 'Rubber', quantity = 60, removeChance = 100 },
            },
            resultItem = { item = 'clip_attachment', name = 'Clip Attachment', description = 'Increases the ammunition capacity of a weapon.', quantity = 1 }
        },
        {
            requiredLevel = 1, addXp = 100, loseXp = 30,
            minigame = 'sequence', difficulty = 'hard',
            craftTime = 25,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 165, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 285, removeChance = 100 },
                { item = 'rubber', name = 'Rubber', quantity = 75, removeChance = 100 },
            },
            resultItem = { item = 'suppressor_attachment', name = 'Suppressor Attachment', description = 'Reduces the sound and muzzle flash of a weapon.', quantity = 1 }
        },
        {
            requiredLevel = 1, addXp = 100, loseXp = 30,
            minigame = 'sequence', difficulty = 'hard',
            craftTime = 25,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 230, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 365, removeChance = 100 },
                { item = 'rubber', name = 'Rubber', quantity = 130, removeChance = 100 },
            },
            resultItem = { item = 'drum_attachment', name = 'Drum Attachment', description = 'A high-capacity drum magazine for a weapon.', quantity = 1 }
        },
        {
            requiredLevel = 1, addXp = 100, loseXp = 30,
            minigame = 'sequence', difficulty = 'hard',
            craftTime = 25,
            ingredients = {
                { item = 'metalscrap', name = 'Metal Scrap', quantity = 255, removeChance = 100 },
                { item = 'steel', name = 'Steel', quantity = 390, removeChance = 100 },
                { item = 'rubber', name = 'Rubber', quantity = 145, removeChance = 100 },
            },
            resultItem = { item = 'smallscope_attachment', name = 'Small Scope Attachment', description = 'A small scope for improved accuracy at range.', quantity = 1 }
        },
    }
}

Config.MiniGameSetting = {
    timingBar = {
        easy    = {speed = 1,   size = 30, time = 8000}, 
        medium  = {speed = 1.5, size = 20, time = 7000}, 
        hard    = {speed = 2,   size = 15, time = 6000}, 
        expert  = {speed = 3,   size = 10, time = 5000}
    },

    sequence = {
        easy    = {len = 3, time = 700, playerTime = 5000}, 
        medium  = {len = 4, time = 600, playerTime = 6000}, 
        hard    = {len = 5, time = 500, playerTime = 7000}, 
        expert  = {len = 7, time = 400, playerTime = 8000}
    },

    rapidClick = {
        easy    = {time = 5, target = 20}, 
        medium  = {time = 4, target = 25}, 
        hard    = {time = 3, target = 30}, 
        expert  = {time = 3, target = 40}
    },

    rhythm = {
        easy    = {speed = 3, tries = 5, size = 20, time = 15000}, 
        medium  = {speed = 4, tries = 4, size = 15, time = 12000}, 
        hard    = {speed = 5, tries = 3, size = 10, time = 10000}, 
        expert  = {speed = 7, tries = 3, size = 8,  time = 8000}
    },

    lockpicking = {
        easy    = {size = 30, time = 20}, 
        medium  = {size = 20, time = 15}, 
        hard    = {size = 15, time = 12}, 
        expert  = {size = 10, time = 10}
    },
}
```


# ak47\_radio

{% embed url="<https://youtu.be/z6LhUhT8pm0>" %}

## Features:

* Fully draggable & resizable modern UI
* Job-restricted secure frequencies (Police/EMS)
* Quick-access Favorites & Recent channel tabs
* Persistent user preferences (Volume, Zoom, Position)
* Real-time connected player list & speaking indicators
* Automatic disconnect logic on death or downed state
* Immersive prop animations & interactive sound effects
* Realistic signal interference & "Signal Breakup" logic
* Adaptive distance-based volume fading & cut-off
* Smart proximity-gated mic click sound effects
* High-performance adaptive threading & idle logic

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* pma-voice: <https://github.com/AvarianKnight/pma-voice/releases/latest>

### How To Install:

1. Download `ak47_radio` from your cfx portal.
2. Add the script in your resources folder.
3. Add radio item based on your framework instruction.
4. Add radio item image in your inventory.
5. Start the script in your server.cfg below framework files.
6. Make sure you have all dependencies.
7. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/7162400>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# ak47\_fishing

{% embed url="<https://youtu.be/sZERUjjm8Zk>" %}

### Features:

* Multiple fishing zone
* Level based fishing reward
* Skill system to catch fish
* Level up with fishing
* Stress gain/remove with fishing
* Fish selling shop included
* Buy fishing gear from fish market
* Boat anchor system included

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>

### How To Install:

1. Download `ak47_fishing` from your cfx portal.
2. Add the script in your resources folder.
3. Add items based on your framework instruction.
4. Add item images into inventory.
5. Start the script in your server.cfg below framework files.
6. Make sure you have all dependencies.
7. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/7086711>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# ak47\_garbagejob

{% embed url="<https://youtu.be/8BahU2KVXWU>" %}

**The ultimate multiplayer garbage job experience for FiveM servers.** This script completely revamps sanitation roleplay with seamless multiplayer crew lobbies, dynamic dual dumpster and ambient trash scanning, server-wide synchronized prop removal, an interactive 3D visual unloading bay, and a sleek dark glassmorphic React 18 + Tailwind CSS management UI. Powered by `ak47_lib`, it offers deep career progression, scalable economy balance, anti-leech security, and multi-framework support.

***

### 🔥 Key Features

Designed to maximize roleplay engagement, teamwork, and server economy balancing:

#### 👥 Multiplayer Sanitation Crews & Group Lobby

* **Crews of up to 6:** Work solo or form a sanitation crew with up to 6 players using the in-game UI or slash commands (`/garbage_invite`, `/garbage_kick`, `/garbage_leave`).
* **Teamwork Scaling:** Earn an extra +10% bonus pay and +15% bonus XP / Collection Points per additional crew member.
* **Shared Route Sync:** Live real-time synchronisation of vehicle keys, route waypoints, collected bag counts, and shift earnings across all crew members.

#### 🚛 Dual Scanning Engine & World Prop Cleanup

* **Dual Trash Scanner:** Proximity radar scans both large commercial dumpsters and ambient ground trash props (garbage bags, card piles, debris, wooden crates).
* **Server-Wide Cleanup Sync:** Picked up world trash props are physically hidden and removed across the entire server until restart via `CreateModelHide`.
* **Smooth Animations:** Custom digging animations, prop bone attachments, carrying locomotion, and throw animations into the truck's rear hopper.
* **Dumpster Loot System:** Chance to discover valuable scrap, repair tools, or rare items while searching commercial dumpsters.

#### 📦 3D Visual Unloading Bay & Capacity Shifts

* **Capacity-Based Shifts:** Shifts scale dynamically based on crew size with a minimum target requirement and maximum truck capacity.
* **Overtime & Spotless Bonuses:** Reach the minimum target to clock out early, or keep cleaning until 100% capacity for a +15% overtime pay bonus. 100% clearing an area triggers an instant Spotless Area Cash & XP bonus!
* **3D Visual Unloading Bay:** Drive the loaded truck into the illuminated 3D bounding bay behind the depot, align the heading, and watch the automated hydraulic unloading sequence.
* **Leader Route Switching:** The crew leader can easily cycle to the next dirty zone on the fly by pressing `[G]`.

#### ⭐ Career Progression & Sanitation Rewards Shop

* **6 Rank Tiers:** Level up from *Trainee Scavenger* to *Sanitation Chief*, earning permanent paycheck multipliers (up to +45%).
* **Collection Points:** Earn dedicated Collection Points alongside XP for every bag thrown and every route cleared.
* **Sanitation Rewards Shop:** Spend points directly in the NUI catalog to redeem essential tools, crafting materials, and maintenance kits.
* **Anti-Leech Security:** Built-in validation ensures players must actively load bags or drive the vehicle to receive their shift payout.

#### 👔 Supervisor Dialogue & Modern Glassmorphic UI

* **Interactive NPC Dialogue:** Multi-option dialogue tree with the Sanitation Supervisor to dispatch shifts, change into work attire, access the shop, or collect paychecks.
* **React 18 + Tailwind UI:** Dark glassmorphic interface with 4 dedicated tabs: Operations, Crew Management, Rewards Shop, and Career Hierarchy.
* **Dynamic Inventory Integration:** Automatically fetches item icons from all popular FiveM inventories (`ox_inventory`, `qb-inventory`, `ps-inventory`, `qs-inventory`, etc.).

***

### 💻 Framework:

* ESX, QB, QBX

***

### ⚙️ Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>

***

### ⚙️ How To Install:

* Download ak47\_garbagejob from your cfx portal.
* Add the script into your resources folder.
* Start the script in your `server.cfg` below framework files.
* Make sure you have all dependencies.
* Restart the server.

***

### 🔗 Links & Support:

* **📖 Documentation:** <https://docs.menanak47.com/multi-framework/ak47_garbagejob>
* **🛒 Buy Now (Tebex):** <https://menanak47.tebex.io/package/5089721>
* **💬 Discord Support:** <https://discord.gg/menanak47>

***

### 📝 Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# ak47\_rentalcar

{% embed url="<https://youtu.be/YnYLrLitzkw>" %}

### 🔥 Key Features

Designed to improve roleplay immersion and economy balancing:

#### 🚗 Sleek & Interactive UI

* **Modern Design:** A highly responsive glass-card UI displaying vehicle stats like speed, seats, and pricing.
* **Smart Filtering:** Sort vehicles by Price (Low to High) and filter by categories like Sedans, SUVs, Motorcycles, and more.

#### 💰 Advanced Economy & Deposits

* **Security Deposits:** Requires players to put down a security deposit which is partially or fully refunded upon return.
* **Damage Deductions:** Accurately calculates body and engine damage upon return and deducts repair costs from the deposit.
* **Late Fines:** configurable penalties that charge players for returning their rental vehicle past the expiration time.

#### 📄 Item-Based Rental Papers

* **Physical Contracts:** Players receive a "Rental Paper" item in their inventory containing the vehicle's plate, duration, and expiration details.
* **Transferable:** Give your rental document to a friend, or show it to the police as proof of valid registration.

#### 🗺️ Immersive Interactions

* **NPC Dialogue:** Engage with the rental ped using an immersive, multi-option dialogue interaction system.
* **Multiple Types:** Pre-configured locations for Cars, Boats, and Helicopters with unique spawn points and deposits.
* **Target Support:** Out-of-the-box support for `ox_target`, `qb-target`, and `qtarget`.

#### 🛠️ Technical Excellence

* **Multi-Framework:** Seamlessly works with ESX, QB, and QBX.
* **Highly Secure:** Built-in server-side exploit checks to prevent cheating and unauthorized spawning.

***

### 💻 Framework:

* ESX, QB, QBX

***

### ⚙️ Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>

***

### ⚙️ How To Install:

* Download ak47\_rentalcar from your cfx portal.
* Add the script in your resources folder.
* Add items based on your framework instruction (found in INSTALL ME FIRST).
* Add item images into inventory.
* Start the script in your server.cfg below framework files.
* Make sure you have all dependencies.
* Restart the server.

***

### 🔗 Links & Support:

* **🛒 Buy Now (Tebex):** <https://menanak47.tebex.io/package/7331214>
* **💬 Discord Support:** <https://discord.gg/menanak47>

***

### 📝 Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading


# ak47\_moneywash

{% embed url="<https://youtu.be/mKnF3wUOjMg>" %}

**The ultimate money laundering experience for FiveM servers.** This script completely revamps black money processing with highly interactive laundry machines, dynamic props, realistic timers, and a secure access card system. Powered by `ak47_lib`, it features multi-framework support, custom tax rates, target interactions, and advanced timeout mechanics.

***

### 🔥 Key Features

Designed to improve roleplay immersion and economy balancing:

#### 💳 Secure Access & Multi-Machine Limits

* **Laundry Cards:** Players must possess and insert a specific "Laundry Card" item to interact with and unlock the machines.
* **Usage Limits:** Prevent monopoly by limiting the maximum number of machines a single player can operate simultaneously.

#### ⏱️ Dynamic Washing & Tax System

* **Time is Money:** Wash times are dynamically calculated based on the exact amount of money deposited (e.g., 30 seconds per $10k).
* **Configurable Taxes:** Control the economy by setting a custom tax percentage that the machine takes as a cut (e.g., player receives 80% of clean cash).
* **Metadata Support:** Fully supports item metadata (like QB/QBX `markedbills` worth) alongside standard black money accounts.

#### 🛑 Advanced Timeout & Cooldown Mechanics

* **Abandonment Penalties:** If a player forgets to collect their washed money within a set time limit, the money is lost, and the machine goes into cooldown.
* **Machine Cooldowns:** Machines require a configured resting period after a cycle finishes before they can be used again.

#### 🎭 Immersive Visuals & Props

* **Dynamic Props:** The physical washing machine props change based on their state (Idle, Spinning, Finished, Opened).
* **Audio & Visual Cues:** Built-in audio cues alert nearby players when a wash is done. Owners get detailed 2D screen text overlays, while bystanders see localized 3D text.

#### 🛠️ Technical Excellence

* **Multi-Framework & Target Support:** Seamlessly works with ESX, QB, QBX and various target systems (ox\_target, qb-target, qtarget).
* **Highly Secure:** Built-in server-side exploit checks, including distance validation and trigger authentication, to prevent cheating or duping.

***

### 💻 Framework:

* ESX, QB, QBX

***

### ⚙️ Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* [ak47\_target](https://github.com/MenanAk47/ak47_target)/ox\_target/qb-target/qtarget

***

### ⚙️ How To Install:

1. Download ak47\_moneywash from your cfx portal.
2. Add the script in your resources folder.
3. Add card item based on your framework instruction.
   * esx: import esx.sql in your server database.
   * qb: add items in qb-core/shared/items.lua .
   * qbx: add items in your inventory items.lua .
4. Add item images into your inventory.
5. Start the script in your server.cfg below framework files.
6. Make sure you have all dependencies.
7. Restart the server.

***

### 🔗 Links & Support:

* **🛒 Buy Now (Tebex):** <https://menanak47.tebex.io/package/5572011>
* **💬 Discord Support:** <https://discord.gg/menanak47>

***

### 📝 Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# ak47\_playershop

{% embed url="<https://youtu.be/3GSgQD-TCY0>" %}

{% embed url="<https://youtu.be/5CENFo5vMtQ>" %}

### Features

* Create fully functional shops directly inside the game using admin tools.
* Any player can purchase available shops to start their business.
* Select specific items to sell using a clean in-game UI.
* Owners can set individual prices for every item in their shop.
* Set a main shipment location for the shop to collect stock.
* Configure unique shipment locations for specific items (e.g., weapons at the depot, food at the docks).
* Shipment costs are calculated as a configurable percentage of the item's value.
* Toggle between an automated NPC shopkeeper or a manual player-run shop.
* Admin option to enable infinite stock for specific shops.
* Hire real players to work at the shop.
* Manage specific access for each employee (Doors, Garage, Stock, Bank, etc.).
* Set and update salaries for your employees.
* Purchase upgrades to increase stock capacity and unlock better vehicles.
* Sell the shop back to the market, receiving a percentage of the value including all installed upgrades.
* Built-in security system allowing owners and employees to lock/unlock shop doors.

### Framework:

* ESX, QB, QBX

### Dependencies:

* ox\_lib: <https://github.com/overextended/ox_lib/releases/latest>
* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* PolyZone: <https://github.com/mkafrin/PolyZone/releases/latest>
* ox\_target/qb-target/qtarget

### How To Install:

1. Download `ak47_playershop` from your cfx portal.
2. Add the script in your resources folder.
3. Import `database.sql` in your server database.
4. Start the script in your `server.cfg` below framework files.
5. Make sure you have all dependencies.
6. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/5988560>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# Exports

If player is near a shop point, this export will open the closest shop.

```lua
exports['ak47_playershop']:OpenClosestShop()
```


# ak47\_banking

{% embed url="<https://youtu.be/UD9C23X6G0s>" %}

### Features

1. Modern, interactive Banking UI
2. Physical ATM Cards with PIN system and Tiers (Basic, Gold, Platinum, Black)
3. Dynamic Stock Market and Crypto Trading with crashes, FOMO pumps, and volatility
4. Advanced Loan system linked to a dynamic Credit Score
5. Automated Wealth Tax and Income Tax System
6. Shared Accounts, Job/Society Accounts, and Beneficiaries
7. Full Admin Panel to manage economy, loans, and city treasury
8. Police tools to search accounts and flag for tax evasion
9. Comprehensive Discord Webhook logging
10. Seamlessly works with ESX, QB & QBX
11. Built-in server-side exploit checks
12. Multi-target system support (ak47\_target, ox\_target, qb-target, qtarget)

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* [ak47\_target](https://github.com/MenanAk47/ak47_target)/ox\_target/qb-target/qtarget

### How To Install:

1. Download `ak47_banking` from your cfx portal.
   * <https://portal.cfx.re/assets/granted-assets>
2. Add the script in your resources folder.
3. Import `database.sql` in your server database.
4. Add card items based on your framework instruction.
   * esx: import `esx.sql` in your server database.
   * qb: add items in qb-core/shared/items.lua .
   * qbx: add items in your inventory items.lua .
5. Add item images in your inventory.
6. Start the script in your `server.cfg` below framework core.
7. Make sure you have all dependencies.
8. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/7382727>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# Exports


# Client

​These functions are client-side and can be accessed using the standard FiveM export syntax. They are highly useful for integrating `ak47_banking` into third-party interaction scripts, such as `ox_target`, `qb-target`, or custom radial menus.

#### OpenNearestAtm

Attempts to open the ATM interface for the player. It automatically communicates with the server to check if the player has a valid ATM card in their inventory and if it can be used. If successful, it opens the UI and caches the active card data for the transaction.

| **Parameter** | **Type** | **Description**                              |
| ------------- | -------- | -------------------------------------------- |
| *(None)*      | `N/A`    | This export does not require any parameters. |

Returns: `boolean` (Returns `true` if the ATM UI successfully opened, `false` if they lack a card or the interaction failed)

```lua
-- Example: Opening an ATM via a custom target script or command
local success = exports['ak47_banking']:OpenNearestAtm()

if not success then
    -- The script already notifies the player, but you can add custom logic here
    print("Failed to open ATM: No card found or card cannot be used.")
end
```

#### OpenNearestBank

Checks if the player is currently near one of the configured bank locations (from `Config.BankLocations`). If they are within the allowed distance, it opens the main Bank UI. If they are too far away, it automatically triggers an error notification.

| **Parameter** | **Type** | **Description**                                                                                  |
| ------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `maxDistance` | `number` | *(Optional)* The maximum distance to check for a bank location. Defaults to `5.0` if left blank. |

Returns: `void` (No return value)

```lua
-- Example 1: Opening a bank with the default 5.0 distance
exports['ak47_banking']:OpenNearestBank()

-- Example 2: Using a stricter distance limit (e.g., 2.5) for a specific target zone
exports['ak47_banking']:OpenNearestBank(2.5)
```


# Server

### 🔗 How to use Exports

All functions are server-side and can be accessed using the standard FiveM export syntax:

```lua
exports['ak47_banking']:FunctionName(parameters)
```

***

### 👤 Player Banking & Transactions

These functions allow you to safely manipulate a player's personal bank account without creating "silent transactions." Using these exports ensures the transaction appears in the player's UI.

#### `AddPlayerBankMoney`

Adds money to a player's personal bank account and logs the transaction.

| **Parameter** | **Type**  | **Description**                  |
| ------------- | --------- | -------------------------------- |
| `source`      | `integer` | The server ID of the player.     |
| `amount`      | `number`  | The amount to deposit.           |
| `reason`      | `string`  | The description shown in the UI. |

Returns: `boolean` (true if successful)

```lua
-- Example: Paying a player a specific bonus
local success = exports['ak47_banking']:AddPlayerBankMoney(source, 5000, "Lottery Winnings")
```

#### `RemovePlayerBankMoney`

Removes money from a player's personal bank account (fails if they have insufficient funds).

| **Parameter** | **Type**  | **Description**                  |
| ------------- | --------- | -------------------------------- |
| `source`      | `integer` | The server ID of the player.     |
| `amount`      | `number`  | The amount to withdraw.          |
| `reason`      | `string`  | The description shown in the UI. |

Returns: `boolean`, `string` (success status, and an error message if failed)

```lua
-- Example: Charging a player for a vehicle purchase
local success, msg = exports['ak47_banking']:RemovePlayerBankMoney(source, 25000, "Vehicle Purchase: Panto")
if not success then
    print("Failed: " .. msg) -- "Insufficient funds"
end
```

#### `LogCustomTransaction`

Manually push a transaction log to a player's UI with a custom icon and color. Useful if you handle the money logic elsewhere but want it on their bank statement.

| **Parameter** | **Type** | **Description**                              |
| ------------- | -------- | -------------------------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player.                |
| `title`       | `string` | Transaction description.                     |
| `amount`      | `number` | Positive for income, negative for expense.   |
| `type`        | `string` | `"deposit"`, `"withdrawal"`, or `"transfer"` |
| `icon`        | `string` | Lucide Icon name (e.g., `"Car"`, `"Home"`)   |
| `color`       | `string` | Tailwind color class (e.g., `"bg-red-500"`)  |

```lua
-- Example: Logging a house purchase with a custom house icon
exports['ak47_banking']:LogCustomTransaction("CID12345", "Purchased Property", -150000, "withdrawal", "Home", "bg-purple-500")
```

***

### 🏢 Business & Shared Accounts

Interact with Job, Gang, or custom Shared player accounts.

#### `GetAccountBalance`

Returns the current balance of any business or shared account.

| **Parameter** | **Type** | **Description**                                           |
| ------------- | -------- | --------------------------------------------------------- |
| `accountName` | `string` | The name of the account (e.g., `"police"`, `"mechanic"`). |

Returns: `number`

```lua
local mechFunds = exports['ak47_banking']:GetAccountBalance("mechanic")
print("The mechanic shop has $" .. mechFunds)
```

#### `AddMoney` / `RemoveMoney`

Adds or removes money from a business/shared account.

| **Parameter** | **Type** | **Description**             |
| ------------- | -------- | --------------------------- |
| `accountName` | `string` | The name of the account.    |
| `amount`      | `number` | The amount to transact.     |
| `reason`      | `string` | Reason for the transaction. |

Returns: `boolean`

```lua
-- Example: Charging a business for importing supplies
local success = exports['ak47_banking']:RemoveMoney("burgershot", 500, "Ingredient Restock")

-- Example: Paying a business from a state grant
local success = exports['ak47_banking']:AddMoney("police", 50000, "State Funding Grant")
```

#### `HasSharedAccountAccess`

Checks if a specific player has permission to use a shared account.

| **Parameter** | **Type** | **Description**                 |
| ------------- | -------- | ------------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player.   |
| `accountName` | `string` | The name of the shared account. |

Returns: `boolean`

```lua
local hasAccess = exports['ak47_banking']:HasSharedAccountAccess(cid, "SmithFamilyFund")
```

***

### 💳 Credit & Debit Cards

#### `GetPlayerCards`

Fetches a list of all cards owned by a player.

| **Parameter** | **Type** | **Description**               |
| ------------- | -------- | ----------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player. |

Returns: `table` (List of card objects)

```lua
local cards = exports['ak47_banking']:GetPlayerCards(cid)
for _, card in ipairs(cards) do
    print("Card ID: " .. card.id .. " | Balance: $" .. card.balance)
end
```

#### `ChargeCard`

Directly charges a specific card. Automatically handles whether the card is prepaid or linked directly to the main bank balance. Excellent for toll booths or vending machines.

| **Parameter** | **Type** | **Description**            |
| ------------- | -------- | -------------------------- |
| `cardId`      | `string` | The unique ID of the card. |
| `amount`      | `number` | The amount to charge.      |
| `reason`      | `string` | Transaction description.   |

Returns: `boolean`, `string`

```lua
-- Example: Highway Toll Booth
local success, msg = exports['ak47_banking']:ChargeCard("C12345678", 15, "Highway Toll")
```

***

### 📈 Credit Score & Loans

#### `GetCreditScore`

Retrieves a player's current credit score.

| **Parameter** | **Type** | **Description**               |
| ------------- | -------- | ----------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player. |

Returns: `number`

```lua
local score = exports['ak47_banking']:GetCreditScore(cid)
if score >= 700 then
    print("Approved for premium financing!")
end
```

#### `UpdateCreditScore`

Modifies a player's credit score (can be positive or negative).

| **Parameter** | **Type** | **Description**               |
| ------------- | -------- | ----------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player. |
| `amount`      | `number` | Amount to add/subtract.       |

Returns: `boolean`

```lua
-- Reward player for paying a custom dealership invoice on time
exports['ak47_banking']:UpdateCreditScore(cid, 5)
```

#### `GetActiveLoans`

Checks a player's debt status. Useful for preventing players from taking out car loans if they are already in massive debt.

Returns: `table` `{ count, totalDebt, loans }`

```lua
local debtInfo = exports['ak47_banking']:GetActiveLoans(cid)
print("Player has " .. debtInfo.count .. " loans totaling $" .. debtInfo.totalDebt)
```

***

### ⚖️ Law Enforcement & Taxes

#### `GetBankFlags` / `AddBankFlag` / `RemoveBankFlag`

Integrate with an external MDT to place freezes, warrants, or flags on a bank account.

```lua
-- Add a flag to an account
exports['ak47_banking']:AddBankFlag(cid, "Suspected Fraud", "Officer Smith")

-- Read flags (Returns a table of flags)
local flags = exports['ak47_banking']:GetBankFlags(cid)

-- Remove a flag
exports['ak47_banking']:RemoveBankFlag(cid, "Suspected Fraud")
```

#### `GetUnpaidTaxes`

Check if a player is evading taxes.

Returns: `table` `{ hasUnpaid, totalOwed, overdueCount, records }`

```lua
local taxData = exports['ak47_banking']:GetUnpaidTaxes(cid)
if taxData.hasUnpaid then
    print("Cannot sell property to tax evaders!")
end
```

***

### 🏛️ City Treasury (Mayor/Gov)

Interact with the central government account.

#### Functions

* `GetCityTreasuryBalance()`
* `AddCityTreasuryFunds(amount, reason)`
* `RemoveCityTreasuryFunds(amount, reason)`

```lua
-- Example: Paying for a city-wide event from the treasury
local success, msg = exports['ak47_banking']:RemoveCityTreasuryFunds(50000, "City Event Funding")
```

***

### 📊 Stock Market Manipulation

#### `ForceStockMarketEvent`

Force a specific stock to "crash" or "boom". Perfect for Heist scripts (e.g., robbing a specific company crashes their stock, or stealing data boosts a rival's stock).

| **Parameter** | **Type** | **Description**                             |
| ------------- | -------- | ------------------------------------------- |
| `symbol`      | `string` | The stock ticker (e.g., `"LSC"`, `"MAZE"`). |
| `eventType`   | `string` | `"crash"` or `"boom"`                       |

Returns: `boolean`, `string`

```lua
-- Example: Player successfully robbed Fleeca Bank
exports['ak47_banking']:ForceStockMarketEvent("FLE", "crash")
```


# ak47\_fraudsystem

{% embed url="<https://youtu.be/UD9C23X6G0s>" %}

### Features

1. Modern, interactive Banking UI
2. Physical ATM Cards with PIN system and Tiers (Basic, Gold, Platinum, Black)
3. Dynamic Stock Market and Crypto Trading with crashes, FOMO pumps, and volatility
4. Advanced Loan system linked to a dynamic Credit Score
5. Automated Wealth Tax and Income Tax System
6. Shared Accounts, Job/Society Accounts, and Beneficiaries
7. Full Admin Panel to manage economy, loans, and city treasury
8. Police tools to search accounts and flag for tax evasion
9. Comprehensive Discord Webhook logging
10. Seamlessly works with ESX, QB & QBX
11. Built-in server-side exploit checks
12. Multi-target system support (ak47\_target, ox\_target, qb-target, qtarget)

### Framework:

* ESX, QB, QBX

### Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>
* [ak47\_target](https://github.com/MenanAk47/ak47_target)/ox\_target/qb-target/qtarget

### How To Install:

1. Download `ak47_banking` from your cfx portal.
   * <https://portal.cfx.re/assets/granted-assets>
2. Add the script in your resources folder.
3. Import `database.sql` in your server database.
4. Add card items based on your framework instruction.
   * esx: import `esx.sql` in your server database.
   * qb: add items in qb-core/shared/items.lua .
   * qbx: add items in your inventory items.lua .
5. Add item images in your inventory.
6. Start the script in your `server.cfg` below framework core.
7. Make sure you have all dependencies.
8. Restart the server.

### Buy & Support:

* Tebex: <https://menanak47.tebex.io/package/7382727>
* Discord: <https://discord.gg/menanak47>

### Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# Exports


# Client

​These functions are client-side and can be accessed using the standard FiveM export syntax. They are highly useful for integrating `ak47_banking` into third-party interaction scripts, such as `ox_target`, `qb-target`, or custom radial menus.

#### OpenNearestAtm

Attempts to open the ATM interface for the player. It automatically communicates with the server to check if the player has a valid ATM card in their inventory and if it can be used. If successful, it opens the UI and caches the active card data for the transaction.

| **Parameter** | **Type** | **Description**                              |
| ------------- | -------- | -------------------------------------------- |
| *(None)*      | `N/A`    | This export does not require any parameters. |

Returns: `boolean` (Returns `true` if the ATM UI successfully opened, `false` if they lack a card or the interaction failed)

```lua
-- Example: Opening an ATM via a custom target script or command
local success = exports['ak47_banking']:OpenNearestAtm()

if not success then
    -- The script already notifies the player, but you can add custom logic here
    print("Failed to open ATM: No card found or card cannot be used.")
end
```

#### OpenNearestBank

Checks if the player is currently near one of the configured bank locations (from `Config.BankLocations`). If they are within the allowed distance, it opens the main Bank UI. If they are too far away, it automatically triggers an error notification.

| **Parameter** | **Type** | **Description**                                                                                  |
| ------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `maxDistance` | `number` | *(Optional)* The maximum distance to check for a bank location. Defaults to `5.0` if left blank. |

Returns: `void` (No return value)

```lua
-- Example 1: Opening a bank with the default 5.0 distance
exports['ak47_banking']:OpenNearestBank()

-- Example 2: Using a stricter distance limit (e.g., 2.5) for a specific target zone
exports['ak47_banking']:OpenNearestBank(2.5)
```


# Server

### 🔗 How to use Exports

All functions are server-side and can be accessed using the standard FiveM export syntax:

```lua
exports['ak47_banking']:FunctionName(parameters)
```

***

### 👤 Player Banking & Transactions

These functions allow you to safely manipulate a player's personal bank account without creating "silent transactions." Using these exports ensures the transaction appears in the player's UI.

#### `AddPlayerBankMoney`

Adds money to a player's personal bank account and logs the transaction.

| **Parameter** | **Type**  | **Description**                  |
| ------------- | --------- | -------------------------------- |
| `source`      | `integer` | The server ID of the player.     |
| `amount`      | `number`  | The amount to deposit.           |
| `reason`      | `string`  | The description shown in the UI. |

Returns: `boolean` (true if successful)

```lua
-- Example: Paying a player a specific bonus
local success = exports['ak47_banking']:AddPlayerBankMoney(source, 5000, "Lottery Winnings")
```

#### `RemovePlayerBankMoney`

Removes money from a player's personal bank account (fails if they have insufficient funds).

| **Parameter** | **Type**  | **Description**                  |
| ------------- | --------- | -------------------------------- |
| `source`      | `integer` | The server ID of the player.     |
| `amount`      | `number`  | The amount to withdraw.          |
| `reason`      | `string`  | The description shown in the UI. |

Returns: `boolean`, `string` (success status, and an error message if failed)

```lua
-- Example: Charging a player for a vehicle purchase
local success, msg = exports['ak47_banking']:RemovePlayerBankMoney(source, 25000, "Vehicle Purchase: Panto")
if not success then
    print("Failed: " .. msg) -- "Insufficient funds"
end
```

#### `LogCustomTransaction`

Manually push a transaction log to a player's UI with a custom icon and color. Useful if you handle the money logic elsewhere but want it on their bank statement.

| **Parameter** | **Type** | **Description**                              |
| ------------- | -------- | -------------------------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player.                |
| `title`       | `string` | Transaction description.                     |
| `amount`      | `number` | Positive for income, negative for expense.   |
| `type`        | `string` | `"deposit"`, `"withdrawal"`, or `"transfer"` |
| `icon`        | `string` | Lucide Icon name (e.g., `"Car"`, `"Home"`)   |
| `color`       | `string` | Tailwind color class (e.g., `"bg-red-500"`)  |

```lua
-- Example: Logging a house purchase with a custom house icon
exports['ak47_banking']:LogCustomTransaction("CID12345", "Purchased Property", -150000, "withdrawal", "Home", "bg-purple-500")
```

***

### 🏢 Business & Shared Accounts

Interact with Job, Gang, or custom Shared player accounts.

#### `GetAccountBalance`

Returns the current balance of any business or shared account.

| **Parameter** | **Type** | **Description**                                           |
| ------------- | -------- | --------------------------------------------------------- |
| `accountName` | `string` | The name of the account (e.g., `"police"`, `"mechanic"`). |

Returns: `number`

```lua
local mechFunds = exports['ak47_banking']:GetAccountBalance("mechanic")
print("The mechanic shop has $" .. mechFunds)
```

#### `AddMoney` / `RemoveMoney`

Adds or removes money from a business/shared account.

| **Parameter** | **Type** | **Description**             |
| ------------- | -------- | --------------------------- |
| `accountName` | `string` | The name of the account.    |
| `amount`      | `number` | The amount to transact.     |
| `reason`      | `string` | Reason for the transaction. |

Returns: `boolean`

```lua
-- Example: Charging a business for importing supplies
local success = exports['ak47_banking']:RemoveMoney("burgershot", 500, "Ingredient Restock")

-- Example: Paying a business from a state grant
local success = exports['ak47_banking']:AddMoney("police", 50000, "State Funding Grant")
```

#### `HasSharedAccountAccess`

Checks if a specific player has permission to use a shared account.

| **Parameter** | **Type** | **Description**                 |
| ------------- | -------- | ------------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player.   |
| `accountName` | `string` | The name of the shared account. |

Returns: `boolean`

```lua
local hasAccess = exports['ak47_banking']:HasSharedAccountAccess(cid, "SmithFamilyFund")
```

***

### 💳 Credit & Debit Cards

#### `GetPlayerCards`

Fetches a list of all cards owned by a player.

| **Parameter** | **Type** | **Description**               |
| ------------- | -------- | ----------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player. |

Returns: `table` (List of card objects)

```lua
local cards = exports['ak47_banking']:GetPlayerCards(cid)
for _, card in ipairs(cards) do
    print("Card ID: " .. card.id .. " | Balance: $" .. card.balance)
end
```

#### `ChargeCard`

Directly charges a specific card. Automatically handles whether the card is prepaid or linked directly to the main bank balance. Excellent for toll booths or vending machines.

| **Parameter** | **Type** | **Description**            |
| ------------- | -------- | -------------------------- |
| `cardId`      | `string` | The unique ID of the card. |
| `amount`      | `number` | The amount to charge.      |
| `reason`      | `string` | Transaction description.   |

Returns: `boolean`, `string`

```lua
-- Example: Highway Toll Booth
local success, msg = exports['ak47_banking']:ChargeCard("C12345678", 15, "Highway Toll")
```

***

### 📈 Credit Score & Loans

#### `GetCreditScore`

Retrieves a player's current credit score.

| **Parameter** | **Type** | **Description**               |
| ------------- | -------- | ----------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player. |

Returns: `number`

```lua
local score = exports['ak47_banking']:GetCreditScore(cid)
if score >= 700 then
    print("Approved for premium financing!")
end
```

#### `UpdateCreditScore`

Modifies a player's credit score (can be positive or negative).

| **Parameter** | **Type** | **Description**               |
| ------------- | -------- | ----------------------------- |
| `citizenid`   | `string` | The Citizen ID of the player. |
| `amount`      | `number` | Amount to add/subtract.       |

Returns: `boolean`

```lua
-- Reward player for paying a custom dealership invoice on time
exports['ak47_banking']:UpdateCreditScore(cid, 5)
```

#### `GetActiveLoans`

Checks a player's debt status. Useful for preventing players from taking out car loans if they are already in massive debt.

Returns: `table` `{ count, totalDebt, loans }`

```lua
local debtInfo = exports['ak47_banking']:GetActiveLoans(cid)
print("Player has " .. debtInfo.count .. " loans totaling $" .. debtInfo.totalDebt)
```

***

### ⚖️ Law Enforcement & Taxes

#### `GetBankFlags` / `AddBankFlag` / `RemoveBankFlag`

Integrate with an external MDT to place freezes, warrants, or flags on a bank account.

```lua
-- Add a flag to an account
exports['ak47_banking']:AddBankFlag(cid, "Suspected Fraud", "Officer Smith")

-- Read flags (Returns a table of flags)
local flags = exports['ak47_banking']:GetBankFlags(cid)

-- Remove a flag
exports['ak47_banking']:RemoveBankFlag(cid, "Suspected Fraud")
```

#### `GetUnpaidTaxes`

Check if a player is evading taxes.

Returns: `table` `{ hasUnpaid, totalOwed, overdueCount, records }`

```lua
local taxData = exports['ak47_banking']:GetUnpaidTaxes(cid)
if taxData.hasUnpaid then
    print("Cannot sell property to tax evaders!")
end
```

***

### 🏛️ City Treasury (Mayor/Gov)

Interact with the central government account.

#### Functions

* `GetCityTreasuryBalance()`
* `AddCityTreasuryFunds(amount, reason)`
* `RemoveCityTreasuryFunds(amount, reason)`

```lua
-- Example: Paying for a city-wide event from the treasury
local success, msg = exports['ak47_banking']:RemoveCityTreasuryFunds(50000, "City Event Funding")
```

***

### 📊 Stock Market Manipulation

#### `ForceStockMarketEvent`

Force a specific stock to "crash" or "boom". Perfect for Heist scripts (e.g., robbing a specific company crashes their stock, or stealing data boosts a rival's stock).

| **Parameter** | **Type** | **Description**                             |
| ------------- | -------- | ------------------------------------------- |
| `symbol`      | `string` | The stock ticker (e.g., `"LSC"`, `"MAZE"`). |
| `eventType`   | `string` | `"crash"` or `"boom"`                       |

Returns: `boolean`, `string`

```lua
-- Example: Player successfully robbed Fleeca Bank
exports['ak47_banking']:ForceStockMarketEvent("FLE", "crash")
```


# Walkthrough

This guide covers the complete mission flow for both Criminals and Police. It is designed to be quick, easy to read, and covers all possible variations.

> \[!NOTE] This guide is broken down into two main sections: **The Criminal Side** and **The Police Side**.

***

### 😈 The Criminal Side

Your goal is to steal card data, process it in a hidden lab, and cash it out for profit.

#### 1. Preparation (Buying Supplies)

Before you start, you need gear. Head to one of the **Blackmarket Suppliers** (e.g., Davis Alley or the Scrapyard).

**Essential Items to Buy:**

* **MSR Skimmer** or **Advanced Skimmer**: For stealing data at ATMs.
* **Data USB**: To store the stolen data.
* **Fraud Generator**, **Jerry Can**: To power your lab.
* **Hacking Laptop**, **Card Embosser (Printer)**: To process the stolen data.
* **Blank Magnet Cards** & **Blank Check Slips**: To print your fake cards and checks.

> \[!TIP] **Optional Gear:** The **Camera Jammer** reduces the chance of police alerts by 50%. The **RFID Skimmer** lets you steal data wirelessly from players.

#### 2. Data Collection (Skimming)

You need to install your skimmer on an ATM to steal credit card data.

1. **Find an ATM** anywhere in the city.
2. Use your **Skimmer** or **Advanced Skimmer** item.
3. Wait for data to collect:
   * **Passive (NPC) Data:** Takes 5 minutes (or 2.5 mins with Advanced Skimmer).
   * **Active (Player) Data:** Real players using the ATM will be skimmed instantly!
4. **Retrieve Data:** Go back to the ATM and retrieve your skimmer. If enough time has passed, the stolen data will be loaded onto your **Data USB**.

> \[!WARNING] Skimmer batteries die after 30-60 minutes. Retrieve them before they die, or the data is lost!

#### 3. The Lab (Data Processing)

Now you need to decrypt the stolen data and print fake cards.

1. **Set up the Lab:** Find a hidden location.
2. Place the **Fraud Generator** down. Use a **Jerry Can** to start it.
3. Place the **Hacking Laptop** and **Card Embosser** nearby (within 10 meters of the generator).
4. **Decrypt Data:** Use the Laptop. Insert your loaded **Data USB** and complete the hacking minigame. *(Success gives you decrypted data ready to print. Sometimes you might extract raw Crypto instead!)*
5. **Print Fakes:** Use the Card Embosser. Combine the decrypted data with a **Blank Magnet Card** or **Blank Check Slip** to print a Cloned Card or a Forged Check. Complete the minigame to finish printing.

> \[!CAUTION] Generators degrade over time and can break! You will need to repair them to keep power running.

#### 4. Cashing Out (Making Money)

You have fake cards/checks. Here are the ways to turn them into cash:

**Option A: ATM Cashout (Medium Risk)**

* Go to any ATM and use your **Cloned Card**.
* Complete the minigame to withdraw cash.
* *Risk:* If you fail the minigame, the card declines and alerts the police.

**Option B: Retail Fraud (Low/Medium Risk)**

* Go to Vangelico Jewelry and swipe your card for luxury goods (Rolex, Diamond Rings, Gold Chains).
* *Risk:* 25% chance the card declines and alerts the police.

**Option C: Check Fraud (High Risk, High Reward)**

* Take a **Forged Check** to the Pacific Standard Bank teller.
* Hand it in for a massive payout.
* *Risk:* 15% chance the teller realizes it's fake and triggers a silent police alarm.

**Option D: Online Wire Transfer (Safest)**

* Use your **Hacking Laptop** while powered to do a direct online wire transfer.
* Safely moves funds directly to your account.

**Option E: Standalone Fence (Easiest)**

* Go to the NPC Fence and sell your Cloned Cards directly for raw cash.

***

#### 🚀 Advanced Criminal Strategy: ATM Malware & Jackpotting

Instead of normal skimming, you can attack the ATM software directly.

* **Silent Infection:** Install malware wirelessly via Bluetooth. It stays active for 30 minutes and automatically downloads up to 5 cards directly to your device as long as you stay within 8 meters.
* **Loud Jackpotting:** The ultimate heist. Play a very difficult minigame to force the ATM to dispense raw cash ($5000 - $15000).
  * *Warning:* 50% chance of an INSTANT police alert the moment you start!

***

***

### 🚓 The Police Side

Your job is to stop the cybercriminals and respond to fraud alerts.

#### 1. Cybercrime Monitoring

* Get into a **Police Vehicle**.
* Access the **Cybercrime Monitoring Dashboard**. This allows you to track recent fraud activity and hotspots around the city.

#### 2. Responding to Dispatch Alerts

You will receive dispatch alerts when criminals make mistakes. Look out for:

* **Failed ATM Withdrawals:** Someone tried to use a cloned card and messed up the pin/minigame.
* **Suspicious Bank Activity:** A teller caught someone trying to cash a forged check.
* **Declined Luxury Purchases:** A stolen card was swiped at Vangelico Jewelry and declined.
* **ATM Jackpotting:** A loud, high-priority alert that someone is forcing an ATM to spit out cash.

#### 3. Investigation & Seizure

* **Seize Skimmers:** If you find a skimmer attached to an ATM, you can confiscate it. This removes the device and heavily damages the criminal's banking credit score.
* **Bust the Labs:** Criminals need physical props (Generators, Laptops, Printers) to process data. Listen for generators running in alleyways, abandoned buildings, or motels. You can seize their equipment if you find their lab!


# ak47\_hud

{% embed url="<https://youtu.be/RTgw8OED7wM>" %}

### 🔥 Key Features

Designed to improve roleplay immersion and give players total control over their interface:

#### 🎨 Advanced Customization & Community Hub

* Drag-and-Drop Editor: Fully reposition, scale, and adjust the opacity of any HUD element on your screen with a helpful grid overlay.
* Granular Controls: Change individual colors and shapes through an intuitive React-based settings menu.
* Community Presets: Access the built-in Community Hub to browse, vote on, preview, and instantly import custom HUD layouts created by other players.
* Bulk Toggles: Quickly enable or disable entire categories of the UI (e.g., hiding all vehicle elements or all survival stats).

#### 💬 Comprehensive Roleplay Chat

* Advanced Features: Includes group channels, an emoji picker, and a right-click context menu for quick actions (DM, transfer group ownership, kick, report).
* 3D Roleplay Bubbles: Immersive `/me` and `/do` actions displayed natively over player character models in the 3D world.
* Command Suggestions: Dynamic auto-complete and suggestions for standard and custom commands to help new players.

#### 🚗 Dynamic Vehicle Mechanics

* Adaptive Speedometers: Specialized SVG gauges that change automatically depending on the vehicle type (cars, helicopters, boats, cycles, and emergency vehicles).
* Police Radar: Built-in functional radar with front/rear scanning, patrol limits, and target speed locking.
* Seatbelt & Nitro Logic: Integrated seatbelt system featuring physics-based crash ejections, plus built-in nitrous syncing for exhaust flames.

#### 📊 Player Status & Information

* Survival Stats: Track health, armor, hunger, thirst, and dynamic stress that induces sickness visual effects when too high.
* Economy Overview: Sleek floating display for cash, bank, society funds, and current job info.
* Extra Utilities: Animated killfeed, customizable crosshairs with ammo tracking, a dynamic bar compass, and a location box featuring postal codes.

#### 🛠️ Technical Excellence

* Multi-Framework: Seamlessly works with ESX, QB, and QBX out of the box.
* Optimized Rendering: Uses React & Redux state persistence for a buttery smooth UI that remembers player layouts across sessions without hammering the server.
* Discord Logging: Built-in webhook support to keep track of chat history and system events.

### 💻 Framework:

* ESX, QB, QBX

### ⚙️ Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47_lib/releases/latest>

### ⚙️ How To Install:

* Download `ak47_hud` from your cfx portal.
* Add the script in your resources folder.
* Add this resource at the bottom of your server.cfg, below your framework files.
* Ensure you have all dependencies installed and started first.
* (Optional) Follow the `must read.txt` instructions to load chat-suggestions properly if utilizing the chat module.
* Restart the server.

### 🔗 Links & Support:

* 📖 Documentation: <https://docs.menanak47.com/multi-framework/ak47_hud>
* 🛒 Buy Now (Tebex): <https://menanak47.tebex.io/>
* 💬 Discord Support: <https://discord.gg/MenanAk47>

### 📝 Note:

* We don't support custom frameworks. Make sure you are using official framework updates.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# Postal Map

<figure><img src="https://2088945756-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkHcyR2RbVMcj5NHJ2HEa%2Fuploads%2FL4FtJjwfScDH8GeJtPZA%2Fimage.png?alt=media&amp;token=b00646a2-fdd1-4464-9294-8b39f33d277e" alt=""><figcaption></figcaption></figure>

#### 🗺️ Postal Map Installation & Configuration

The HUD is fully pre-configured to work out-of-the-box with the popular community postal map. Follow the steps below to get it running on your server.

#### 📥 Installation Steps

1. Download the Map: Grab the latest version of the postal map from the official GitHub repository: [Acc-Off/postal-code-map](https://github.com/Acc-Off/postal-code-map).
2. Install the Resource: Extract the downloaded file and place the folder into your server's `resources` directory.
3. Start the Resource: Open your `server.cfg` and add the following line so the map starts when your server boots:

   Plaintext

   ```
   ensure postal-code-map
   ```

   *(Note: Make sure the folder name in your resources directory exactly matches the name you put in your `server.cfg`)*

#### ✏️ Updating Custom Postal Data

If you decide to use a different map or have generated custom postal codes for your server, you will need to update the data file within the HUD so the on-screen location matches your map.

1. Navigate to your `ak47_hud` resource folder.
2. Locate the `postal.json` file and open it using a reliable text editor (such as VS Code or Notepad++).
3. Delete the default contents completely and paste in your new postal JSON data.
4. Save the file and restart the `ak47_hud` script on your server.

> ⚠️ Important: Ensure your new data is formatted as valid JSON before saving. A single missing comma or broken bracket in this file will cause the script to fail to read the postal codes.


# Chat Suggestions

## 💬 Setting up Chat Suggestions

To ensure that chat command auto-complete and dynamic suggestions work perfectly within the custom HUD, you need to install the `chat-suggestions` helper script.

### 🛠️ Installation Steps

**1. Locate the Script** Find the `chat-suggestions` folder included in your download inside `INSTALL ME FIRST`

**2. Add to Resources** Drag and drop the `chat-suggestions` folder into your server's `resources` directory.

**3. Edit `server.cfg`** Open your `server.cfg` file to start the resource. Pay close attention to the load order below.

### ⚠️ Critical Note on Load Order

> 🛑 **IMPORTANT: MUST LOAD BEFORE FRAMEWORK**
>
> You **MUST** start `chat-suggestions` **BEFORE** your core framework (`es_extended`, `qb-core`, or `qbx_core`).
>
> **Why?** The main `ak47_hud` script loads *after* your framework. The `chat-suggestions` script needs to load *first* so it can actively listen for and capture all the chat command suggestions that register when your framework initializes on server startup.

#### ✅ Example `server.cfg` Layout

Make sure your configuration looks similar to this:

```
# 1. Start Chat Suggestions Helper FIRST
ensure chat-suggestions

# 2. Then start your core framework
ensure qb-core  # or es_extended / qbx_core

# ... (other dependencies and scripts) ...

# 3. Finally, start the main HUD
ensure ak47_hud
```


# Configuration Guide

The `ak47_hud` is designed to be highly modular. Whether you want a plug-and-play experience or want to micromanage every pixel and system, the `config.lua` and `presets.lua` files give you absolute control.

Below is a guide on how to configure your new HUD, ranging from basic feature toggles to advanced UI presets.

***

### 🟢 1. Basic Configuration (Global Toggles)

If you only want to use certain parts of the HUD (for example, you already have a chat script or a seatbelt script you prefer), you can easily disable our built-in systems.

Open `config.lua` and locate the **GLOBAL HUD TOGGLES** section.

```lua
Config.UseChat = true
Config.UsePartyFrame = true
Config.UseCrosshair = true
Config.UseSeatBelt = false -- Set to false to use an external seatbelt script
Config.UsePoliceRadar = true
Config.UseCustomizer = true -- Allows players to type /hud

```

* **Community Name:** Don't forget to change `Config.CommunityName = 'Ak47 Server'` to your actual server name so it displays correctly in the player's `/hud` menu!
* **Default UI:** By default, the script hides the native GTA ammo, vehicle name, and street names to prevent overlapping. You can toggle these back on under the **GTA DEFAULT UI ELEMENT** section.

***

### 🔵 2. Tuning Core Systems

#### 😰 Stress System

The stress system uses dynamic multipliers instead of hardcoded checks. You can configure exactly who gets stress and how much.

* **Job Multipliers:** Multiply stress gained based on the job. `0.0` means no stress, `2.0` means double stress.

```lua
Config.Stress.JobStressMultiplier = {
    police = 0.3,    -- Cops get 70% less stress
    ambulance = 0.0, -- EMS get ZERO stress
}

```

* **Complete Exemptions:** If you want to entirely disable stress for certain weapons or vehicle classes (like helicopters or boats), add them to the `NoStress` tables:

```lua
Config.Stress.NoStressVehicleClass = {
    [14] = true, -- Boats
    [15] = true, -- Helicopters
    [16] = true, -- Planes
}

```

#### 🏎️ Seatbelt System

You can configure at what speeds players are ejected through the windshield (`Config.MinSpeedToEjectUnbuckled`).

* **Whitelists/Blacklists:** Use `Config.BlacklistedClasses = {8, 13, 14, 15, 16, 21}` to ensure players don't get a "Press B to buckle" prompt while riding a bicycle or flying a plane.

#### 💬 Chat Channels & Templates

Under `Config.ChatSubChannels`, you can edit the icons and colors of the chat tabs (OOC, Me, Do, 911). You can also completely redesign how messages look by editing the HTML inside `Config.ChatTemplates`.

***

### 🟣 3. Customizer Access (Permissions)

By default, players can type `/hud` to open a massive customization menu. As a server owner, you might want to restrict what they can change to maintain a consistent server theme.

Locate `Config.CustomizerAccess`.

* **Bulk Access:** Disabling items here removes the entire category from the `/hud` menu.
* **Single Access:** This allows you to micromanage specific settings.
* *Example:* You want players to be able to change the color of their Minimap, but you want to FORCE the minimap to stay round and a specific size. You would change `minimapShape = false`, `height = false`, and `width = false` inside `Config.CustomizerAccess.Single.minimap`.

***

### 🔴 4. Advanced: Forced UI Overwrites

If you want to force a specific UI style on **all players** (overriding their personal saved settings), you will use the `Config.SettingOverwrite` section at the bottom of `config.lua`.

**How it works:** By default, everything in this section is commented out (with `--`). This means the script uses the player's saved NUI preferences. If you uncomment a line, you **lock** that setting server-wide.

**Example: Forcing a specific Status HUD style** If you want everyone on the server to use the "water" fill style with square icons, you would uncomment those specific lines:

```lua
    statusHud = {
        -- show = true,
        displayStyle = "icon", 
        -- size = 70,
        radius = 4, -- Forces square corners
        -- borderSize = 2,
        fillStyle = "water", -- Forces the water animation
    },

```

***

### 🌟 5. Server Presets (Fast Configs)

Instead of forcing a single style on everyone, you can create **Server Presets**. These act as "Themes" that players can select with one click inside their `/hud` menu (e.g., "Dark Mode", "Neon Theme", "Minimalist").

To add a preset, open the **`presets.lua`** file.

Here is a template for creating a new preset. You define the ID, the Display Name, and then pass any UI settings you want that preset to change:

```lua
table.insert(Config.ServerPresets, {
    id = "cyberpunk_theme",
    name = "Cyberpunk Neon",
    hudItems = {
        -- Make the minimap square with a pink border
        minimap = {
            minimapShape = "squaremap"
        },
        minimapRing = {
            color1 = "#ff00ff"
        },
        -- Turn the health and armor bars into a glowing neon style
        health = {
            color1 = "#ff00ff",
            color2 = "#200020",
            fillStyle = "water"
        },
        armor = {
            color1 = "#00ffff",
            color2 = "#002020",
            fillStyle = "water"
        },
        -- Customize the speedo
        vehicleDigital = {
            speedUnit = "KMH",
            color1 = "#ff00ff"
        }
    }
})

```

**How to find the setting names:** Look at the `Config.SettingOverwrite` section in `config.lua`. Every variable listed there (like `color1`, `radius`, `fillStyle`, `shadow`) can be used inside a preset!


# Global Notifications Integration

If you want to route all default framework notifications (like job payouts, item usage, and system alerts) through the sleek `ak47_hud` notification system, you can easily set up a gateway inside your core framework files.

By making these simple changes, your framework will automatically check if the HUD is running. If it is, it uses the custom UI; if you ever stop the HUD, it safely falls back to the default notifications!

Choose your framework below and follow the steps.

### 🟢 ESX Integration

File to Edit: `es_extended/client/functions.lua`

1. Open the file and search for `function ESX.ShowNotification`.
2. Delete that entire function, along with `function ESX.ShowAdvancedNotification` right below it.
3. Replace them with the following code:

```lua
function ESX.ShowNotification(message, notifyType, length, title, position)
    if GetResourceState('ak47_hud') == 'started' then
        return exports['ak47_hud']:Notify(message, notifyType, length, title)
    end
    return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType or "info", length or 5000, message, title, position)
end

function ESX.ShowAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
    if GetResourceState('ak47_hud') == 'started' then
        return exports['ak47_hud']:Notify(msg, 'info', 7000, sender)
    end
    
    AddTextEntry("esxAdvancedNotification", msg)
    BeginTextCommandThefeedPost("esxAdvancedNotification")
    if hudColorIndex then
        ThefeedSetNextPostBackgroundColor(hudColorIndex)
    end
    EndTextCommandThefeedPostMessagetext(textureDict, textureDict, false, iconType, sender, subject)
    EndTextCommandThefeedPostTicker(flash, saveToBrief == nil or saveToBrief)
end
```

### 🔵 QBCore Integration

File to Edit: `qb-core/client/functions.lua`

1. Open the file and search for `function QBCore.Functions.Notify`.
2. Highlight the entire function and replace it with the following code:

```lua
function QBCore.Functions.Notify(text, texttype, length, icon)
    if GetResourceState('ak47_hud') == 'started' then
        exports['ak47_hud']:Notify(text, texttype, length, icon)
        return
    end
    
    local message = {
        action = 'notify',
        type = texttype or 'primary',
        length = length or 5000,
    }

    if type(text) == 'table' then
        message.text = text.text or 'Placeholder'
        message.caption = text.caption or 'Placeholder'
    else
        message.text = text
    end

    if icon then
        message.icon = icon
    end

    SendNUIMessage(message)
end
```

### 🟣 ox\_lib Integration

File to Edit: `ox_lib/resource/interface/client/notify.lua` *(or search your ox\_lib files for `function lib.notify`)*

1. Open the file and locate `function lib.notify(data)`.
2. Highlight the entire function block and replace it with the following code:

```lua
function lib.notify(data)
    if GetResourceState('ak47_hud') == 'started' then
        exports['ak47_hud']:Notify(data.description, data.type, data.duration, data.title)
        return
    end

    local sound = settings.notification_audio and data.sound
    local payload = table.clone(data)
    payload.sound = nil
    payload.position = payload.position or settings.notification_position

    SendNUIMessage({
        action = 'notify',
        data = payload
    })

    if not sound then return end

    if sound.bank then lib.requestAudioBank(sound.bank) end

    local soundId = GetSoundId()
    PlaySoundFrontend(soundId, sound.name, sound.set, true)
    ReleaseSoundId(soundId)

    if sound.bank then ReleaseNamedScriptAudioBank(sound.bank) end
end
```

> Important Note: Whenever you update `es_extended`, `qb-core`, or `ox_lib` to a new version from their respective GitHub repositories, you may need to re-apply these changes, as updating core files will overwrite your modifications.


# Exports


# Client

The `ak47_hud` provides a robust, developer-friendly export API. These client-side exports allow server owners and other script developers to seamlessly interact with the HUD, toggle its features, and dynamically manage systems like stress, seatbelts, and the police radar without modifying the core code.

> Note: All functions below are Client-Side exports. You call them using `exports['ak47_hud']:FunctionName()`.

### 🖥️ Core HUD API

These exports allow you to control the visibility of the HUD and its individual elements.

#### `ToggleHudElement`

Dynamically shows or hides specific elements of the HUD.

| **Parameter** | **Type**  | **Description**                  |
| ------------- | --------- | -------------------------------- |
| `element`     | `string`  | The ID of the HUD element.       |
| `state`       | `boolean` | `true` to show, `false` to hide. |

Valid Elements: `money`, `status`, `compass`, `location`, `vehicle`, `crosshair`, `party`, `killfeed`, `chat`, `notification`, `serverinfo`, `minimap`, `all`.

```lua
-- Hide just the minimap
exports['ak47_hud']:ToggleHudElement('minimap', false)

-- Hide the entire HUD completely
exports['ak47_hud']:ToggleHudElement('all', false)
```

#### `GetHudState`

Returns the current visibility state of a specific HUD element.

| **Parameter** | **Type** | **Description**                                              |
| ------------- | -------- | ------------------------------------------------------------ |
| `element`     | `string` | The ID of the HUD element (same as above, plus `cinematic`). |

```lua
local isMapVisible = exports['ak47_hud']:GetHudState('minimap')
print("Is Minimap Visible? " .. tostring(isMapVisible))
```

#### `SetCinematicMode`

Forces cinematic mode (black bars) on or off. Hides conflicting HUD elements automatically.

| **Parameter** | **Type**  | **Description**                                      |
| ------------- | --------- | ---------------------------------------------------- |
| `state`       | `boolean` | `true` to enable cinematic bars, `false` to disable. |

```lua
-- Great for cutscenes or custom intros!
exports['ak47_hud']:SetCinematicMode(true)
```

#### `Notify`

Triggers the built-in custom notification system.

| **Parameter** | **Type** | **Description**                                   |
| ------------- | -------- | ------------------------------------------------- |
| `message`     | `string` | The text of the notification.                     |
| `type`        | `string` | `default`, `info`, `success`, `warning`, `error`. |
| `duration`    | `number` | Time in milliseconds (e.g., 5000).                |
| `title`       | `string` | (Optional) Custom title text.                     |

```lua
exports['ak47_hud']:Notify("You have been paid $500", "success", 5000, "Salary")
```

### 🏎️ Seatbelt System API

Manage the seatbelt system, ejection logic, and vehicle whitelists dynamically.

#### `ToggleSeatbeltSystem`

Enables or disables the entire seatbelt and ejection physics system. Useful for minigames or admin modes.

| **Parameter** | **Type**  | **Description**                       |
| ------------- | --------- | ------------------------------------- |
| `state`       | `boolean` | `true` to enable, `false` to disable. |

```lua
exports['ak47_hud']:ToggleSeatbeltSystem(false)
```

#### `SetSeatbelt`

Programmatically buckle or unbuckle the local player.

| **Parameter** | **Type**  | **Description**                           |
| ------------- | --------- | ----------------------------------------- |
| `state`       | `boolean` | `true` to buckle up, `false` to unbuckle. |

<pre class="language-lua"><code class="lang-lua"><strong>-- Force buckle the player (e.g., when using a racing harness item)
</strong>exports['ak47_hud']:SetSeatbelt(true)
</code></pre>

#### `HasSeatbeltOn` & `GetSeatbeltSystemState`

Getter functions to check the status of the seatbelt.

<pre class="language-lua"><code class="lang-lua"><strong>-- Returns true if the player is currently buckled in
</strong>local isBuckled = exports['ak47_hud']:HasSeatbeltOn()

-- Returns true if the ejection/seatbelt logic is currently active in the script
local isSystemActive = exports['ak47_hud']:GetSeatbeltSystemState()
</code></pre>

#### `AddToSeatbeltWhitelist` & `RemoveFromSeatbeltWhitelist`

Dynamically allow specific vehicle classes or models to bypass the seatbelt requirement (no ejection/no seatbelt logic).

| **Parameter**   | **Type**        | **Description**                                        |
| --------------- | --------------- | ------------------------------------------------------ |
| `whitelistType` | `string`        | `"class"` or `"model"`.                                |
| `value`         | `string/number` | Class ID (e.g., `18`) or model name (e.g., `"adder"`). |

```lua
-- Whitelist all emergency vehicles (Class 18) dynamically
exports['ak47_hud']:AddToSeatbeltWhitelist('class', 18)

-- Remove a specific vehicle from the whitelist
exports['ak47_hud']:RemoveFromSeatbeltWhitelist('model', 't20')
```

### 😰 Stress System API

Control stress behaviors, disable certain stress triggers, or whitelist jobs/vehicles on the fly.

#### `ToggleStressSystem`

Toggles specific parts of the stress system on or off.

| **Parameter** | **Type**  | **Description**                                                |
| ------------- | --------- | -------------------------------------------------------------- |
| `system`      | `string`  | `"vehicle"`, `"shooting"`, `"melee"`, `"effects"`, or `"all"`. |
| `state`       | `boolean` | `true` to enable, `false` to disable.                          |

```lua
-- Disable screen shakes and sickness effects (e.g., if a player takes a specific pill)
exports['ak47_hud']:ToggleStressSystem('effects', false)

-- Disable all stress entirely
exports['ak47_hud']:ToggleStressSystem('all', false)
```

#### `GetStressSystemState`

Returns the boolean state of a specific stress sub-system.

| **Parameter** | **Type** | **Description**                                       |
| ------------- | -------- | ----------------------------------------------------- |
| `system`      | `string` | `"vehicle"`, `"shooting"`, `"melee"`, or `"effects"`. |

```lua
local isShootingStressActive = exports['ak47_hud']:GetStressSystemState('shooting')
```

#### `AddToStressBypass` & `RemoveFromStressBypass`

Dynamically exempt specific jobs, vehicles, vehicle classes, or weapons from generating stress.

| **Parameter** | **Type**        | **Description**                                                |
| ------------- | --------------- | -------------------------------------------------------------- |
| `bypassType`  | `string`        | `"job"`, `"vehicle"`, `"class"`, or `"weapon"`.                |
| `value`       | `string/number` | The identifier (e.g., `"ambulance"`, `"WEAPON_STUNGUN"`, etc.) |

```lua
-- Stop players from getting stress while using a taser
exports['ak47_hud']:AddToStressBypass('weapon', 'WEAPON_STUNGUN')

-- Prevent a custom job from generating any stress
exports['ak47_hud']:AddToStressBypass('job', 'mechanic')
```

### 🚓 Police Radar API

Integrate the police radar with radial menus, dispatch systems, or external keybind managers.

#### `OpenRadarUI`

Opens the Police Radar remote control UI programmatically. Bypasses the `/radar` command.

```lua
-- Example: Triggering from a qb-radialmenu callback
exports['ak47_hud']:OpenRadarUI()
```

#### `SetRadarPower`

Turns the radar tracking on or off.

| **Parameter** | **Type**  | **Description**                         |
| ------------- | --------- | --------------------------------------- |
| `state`       | `boolean` | `true` to turn on, `false` to turn off. |

```lua
exports['ak47_hud']:SetRadarPower(true)
```

#### `GetRadarData`

Fetches the live, real-time data currently captured by the radar's antennas.

Returns a table: `{ patrolSpeed = number, front = table, rear = table }`

<pre class="language-lua"><code class="lang-lua"><strong>local data = exports['ak47_hud']:GetRadarData()
</strong>
if data.front.speed > 120 then
    print("Speeding vehicle detected! Plate: " .. data.front.plate)
end
</code></pre>

#### `SetRadarLock`

Locks or unlocks a specific antenna to hold a speed reading.

| **Parameter** | **Type**  | **Description**                    |
| ------------- | --------- | ---------------------------------- |
| `direction`   | `string`  | `"front"` or `"rear"`.             |
| `state`       | `boolean` | `true` to lock, `false` to unlock. |

```lua
-- Lock the front antenna
exports['ak47_hud']:SetRadarLock('front', true)
```

#### `ClearRadarFast`

Clears the saved "Fastest Speed" memory for the antennas.

| **Parameter** | **Type** | **Description**                  |
| ------------- | -------- | -------------------------------- |
| `direction`   | `string` | `"front"`, `"rear"`, or `"all"`. |

```lua
-- Clear all saved fast speeds via a custom external keybind
exports['ak47_hud']:ClearRadarFast('all')
```

#### `GetRadarState`

Gets the current power status and configuration settings of the radar.

Returns a table: `{ power = boolean, settings = table }`

```lua
local radarInfo = exports['ak47_hud']:GetRadarState()
print("Is radar powered on? " .. tostring(radarInfo.power))
```


# Server

The `ak47_hud` server-side API provides powerful tools for external scripts to seamlessly interact with your core systems. These exports allow other resources—such as heist scripts, admin menus, minigames, and racing systems—to bypass standard UI requirements, sync state globally, and manipulate data safely.

> Note: All functions below are Server-Side exports. You must call them from a server script using `exports['ak47_hud']:FunctionName()`.

### 💬 Chat System API

Manage player chat groups programmatically. These exports interact directly with the local cache and database, automatically refreshing the UI for all relevant players without requiring them to type commands or enter passwords.

#### `GetPlayerChatGroup`

Instantly checks the server's cache to see which chat group a player is currently in (0 database queries).

| **Parameter** | **Type** | **Description**         |
| ------------- | -------- | ----------------------- |
| `source`      | `number` | The player's server ID. |

Returns: `string` (The group name) or `nil` if not in a group.

```lua
local groupName = exports['ak47_hud']:GetPlayerChatGroup(source)

if groupName then
    print("Player is currently in group: " .. groupName)
end
```

#### `CreatePlayerGroup`

Creates a brand new chat group via script, saves it to the database, and automatically assigns the target player as the owner.

| **Parameter** | **Type** | **Description**                              |
| ------------- | -------- | -------------------------------------------- |
| `groupName`   | `string` | The desired name for the group.              |
| `password`    | `string` | The group's password.                        |
| `ownerSrc`    | `number` | The server ID of the player who will own it. |

```lua
-- Great for automatically creating a secure group for a heist crew
exports['ak47_hud']:CreatePlayerGroup('FleecaCrew', 'secret123', source)
```

#### `AddPlayerToGroup`

Forces an online player into a specific group. This completely bypasses the password requirement, making it perfect for external script integrations.

| **Parameter** | **Type** | **Description**                     |
| ------------- | -------- | ----------------------------------- |
| `targetSrc`   | `number` | The server ID of the player to add. |
| `groupName`   | `string` | The name of the group.              |

<pre class="language-lua"><code class="lang-lua"><strong>-- Force a player into the 'FleecaCrew' group silently
</strong>exports['ak47_hud']:AddPlayerToGroup(targetPlayerId, 'FleecaCrew')
</code></pre>

#### `RemovePlayerFromGroup`

Removes an online player from whatever group they are currently in. Automatically updates the UI for the removed player and all remaining members.

| **Parameter** | **Type** | **Description**                        |
| ------------- | -------- | -------------------------------------- |
| `targetSrc`   | `number` | The server ID of the player to remove. |

<pre class="language-lua"><code class="lang-lua"><strong>-- Kick a player out of their active group (e.g., when they go off-duty)
</strong>exports['ak47_hud']:RemovePlayerFromGroup(source)
</code></pre>

### 🛠️ Utilities & Systems API

Use these exports to interact with the HUD's notification, kill feed, and vehicle systems programmatically.

#### `SendSystemMessage`

Sends a standardized, formatted system message directly to a specific player's chat window.

| **Parameter** | **Type** | **Description**                                                                             |
| ------------- | -------- | ------------------------------------------------------------------------------------------- |
| `source`      | `number` | The player ID to send the message to.                                                       |
| `message`     | `string` | The text to display.                                                                        |
| `rgb`         | `table`  | *(Optional)* `{R, G, B}` color table. Defaults to a standard system green `{52, 211, 153}`. |

```lua
-- Send a custom red system alert
exports['ak47_hud']:SendSystemMessage(source, "You have entered a restricted zone!", {255, 0, 0})
```

#### `BroadcastCustomKill`

Broadcasts a custom kill to the Kill Feed UI for all players on the server. Ideal for paintball, laser tag, arena minigames, or custom death scripts that bypass native damage events.

| **Parameter** | **Type**        | **Description**                                         |
| ------------- | --------------- | ------------------------------------------------------- |
| `killerName`  | `string`        | Display name of the attacker.                           |
| `killerId`    | `number`        | Server ID of the attacker.                              |
| `victimName`  | `string`        | Display name of the victim.                             |
| `victimId`    | `number`        | Server ID of the victim.                                |
| `weaponHash`  | `number/string` | The weapon hash or weapon name.                         |
| `isHeadshot`  | `boolean`       | `true` to display the headshot icon and play the sound. |

<pre class="language-lua"><code class="lang-lua"><strong>-- Broadcast a custom minigame kill globally
</strong>exports['ak47_hud']:BroadcastCustomKill("SniperGod99", 5, "NoobMaster", 12, "WEAPON_SNIPERRIFLE", true)
</code></pre>

#### `SetVehicleNitro`

Sets the nitro level of a specific vehicle programmatically. This sets the global network state, immediately reflecting on the driver's HUD.

| **Parameter** | **Type** | **Description**                        |
| ------------- | -------- | -------------------------------------- |
| `netId`       | `number` | The network ID of the vehicle.         |
| `amount`      | `number` | The amount of nitro to set (0 to 100). |

```lua
-- Example: A racing script that refills a player's nitro when they hit a checkpoint
local veh = GetVehiclePedIsIn(GetPlayerPed(source), false)
local netId = NetworkGetNetworkIdFromEntity(veh)

exports['ak47_hud']:SetVehicleNitro(netId, 100)
```


# ak47\_multicharacter

{% embed url="<https://www.youtube.com/watch?v=m8pRr0oh7cI>" %}

### 🔥 Key Features

Designed to improve roleplay immersion right from the first connection:

#### 🎬 Cinematic Camera & Ped Lineups

* **Dynamic Lineups:** Interactive 3D ped lineups with customizable scenarios (e.g., `WORLD_HUMAN_SMOKING_POT`) and specific animations for each character slot.
* **Seamless Transitions:** Smooth, buttery dive-bomb camera transitions from the sky down to the final player spawn coordinates.
* **Live Previews:** Custom appearance support for `illenium-appearance`, `qb-clothing`, and `esx_skin` to preview your exact character model and outfits.

#### 🎨 Modern React Interface

* **Beautiful UI:** Sleek, responsive React-based interface displaying detailed character stats (cash, bank, job, rank) alongside auto-generated mugshots.
* **Advanced Creation:** Comprehensive character creation menu with strict validations for First/Last Name, Nationality, Gender, Birthdate, and Height parameters.

#### 🔓 Progression & Integration

* **Discord Slot Unlocking:** Reward your community by linking specific Discord roles to unlock extra character slots automatically upon connecting.
* **Extensive Spawns:** Categorized, keyboard-navigable spawn selection supporting normal locations, `qb-apartments`, `qb-houses`, `ak47_housing`, and `snipe-motel`.

#### 🛠️ Technical Excellence

* **Multi-Framework:** Seamlessly works out-of-the-box with ESX & QBCore adapting database queries dynamically.
* **Admin Utilities:** Built-in server commands like `/logout`, `/closeNUI`, and `/deletechar` for easy administration and cross-table character wiping.
* **Environment Sync:** Automatically pauses time and weather sync (fully compatible with `cd_easytime` and `qb-weathersync`) during the selection phase to maintain atmosphere.

***

### 💻 Framework:

* ESX, QBCore

***

### ⚙️ Dependencies:

* Your chosen framework (ESX/QB)
* Optional: `illenium-appearance`, `qb-clothing`, or `esx_skin` for ped rendering.

***

### ⚙️ How To Install:

* Download ak47\_multicharacter from your cfx portal.
* Add the script in your resources folder.
* Start the script in your server.cfg below framework files.
* Configure your Discord Bot Token and Guild ID in `config-server.lua` if using role-based slots.
* Restart the server.

***

### 🔗 Links & Support:

* **📖 Documentation:** <https://docs.menanak47.com/multi-framework/ak47\\_multicharacter>
* **🛒 Buy Now (Tebex):** <https://menanak47.tebex.io/package/7545540>
* **💬 Discord Support:** <https://discord.gg/MenanAk47>

***

### 📝 Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading.


# Exports

You can interact with **ak47\_multicharacter** from your other server-side scripts using the provided exports. This is highly useful for creating custom admin menus, penalty scripts, or sleep/logout systems.

#### `Logout`

Forces a specific player to log out of their current active character, saves their data, and sends them back to the sky-camera character selection screen.

**Export:**

```lua
exports['ak47_multicharacter']:Logout(source)
```

**Parameters:**

* `source` (number) - The server ID of the player you wish to log out.

**Example Usage:**

```lua
-- Example: A custom bed script where sleeping logs the player out
RegisterNetEvent('my_custom_script:GoToSleep', function()
    local src = source
    -- Perform your save logic here...
    
    -- Send player back to character selection
    exports['ak47_multicharacter']:Logout(src)
end)
```


# Commands

**ak47\_multicharacter** comes with several built-in administrative commands to help you manage player sessions and clean up character data directly from the server.

All commands require the `admin` permission level.

#### `/logout`

Logs the player out of their current character and seamlessly returns them to the multicharacter selection screen without having to drop from the server.

* **Usage:** `/logout`
* **Permission:** Admin Only

#### `/closeNUI`

A failsafe command designed to forcibly close the NUI (user interface) and release mouse/keyboard focus if a player somehow gets stuck during the selection process.

* **Usage:** `/closeNUI`
* **Permission:** Admin Only

#### `/deletechar`

Allows an administrator to completely wipe and delete a specific character from the server. This command dynamically scans your database and removes the character from **all** related tables (e.g., inventory, vehicles, properties) based on the Citizen ID.

* **Usage:** `/deletechar [Citizen ID]`
* **Example:** `/deletechar TEB12345`
* **Arguments:**
  * `Citizen ID` - The unique identifier of the character you wish to delete (e.g., CID for QBCore, or Identifier for ESX).
* **Permission:** Admin Only


# Housing

**ak47\_multicharacter** supports `qb-houses`, `qb-apartments`, `ak47_housing`, and `snipe-motel` natively. If you are using a completely custom housing or motel script, you can easily integrate it by editing the `custom/client.lua` and `custom/server.lua` files.

### Client-Side Overrides (`custom/client.lua`)

Modify these functions to trigger your custom housing events.

#### 1. Initial Spawn (Apartments/Motels)

Handles spawning a player inside their starting apartment upon creating a new character or logging in.

```lua
function Custom_SpawnInsideApartment()
    -- Example for custom motel:
    -- exports["my_custom_motel"]:SpawnInsideApartment()
end
```

#### 2. Creating an Apartment

Triggered when a player finishes character creation and chooses an apartment.

```lua
function Custom_CreateApartment(appType, label)
    -- Example:
    -- TriggerServerEvent("my_apartments:server:CreateApartment", appType, label)
end
```

#### 3. Last Location Spawning

If a player logged out inside a property, these functions ensure they spawn back inside.

```lua
function Custom_SpawnLastLocationHouse(houseId)
    -- Example: TriggerEvent('my_housing:client:EnterHouse', houseId)
end

function Custom_SpawnLastLocationApartment(apartmentType, apartmentId)
    -- Example: TriggerEvent('my_apartments:client:EnterApartment', apartmentType, apartmentId)
end
```

#### 4. Entering Owned Houses (Spawn Menu)

Triggered when a player selects one of their properties from the "My Houses" tab in the UI.

```lua
function Custom_EnterOwnedHouse(location)
    -- Example: TriggerEvent('my_housing:client:EnterOwnedHouse', location)
end
```

### Server-Side Overrides (`custom/server.lua`)

#### 1. Loading House Configurations

This function should fetch all available house coordinates and send them to the client to populate the map/UI.

```lua
function Custom_LoadHouseData(source)
    local src = source
    -- Query your custom housing table and format the data
    -- Send data to client...
end
```

#### 2. Fetching Owned Houses for the UI

This function queries the database to populate the "My Houses" tab in the spawn selector. It must return a table containing the `house` ID and the display `label`.

```lua
function Custom_GetOwnedHouses(source, cid)
    local myHouses = {}
    
    -- Example custom query:
    local houses = MySQL.query.await('SELECT * FROM my_custom_houses WHERE owner = ?', {cid})
    
    if houses then
        for i = 1, #houses do
            myHouses[#myHouses+1] = {
                house = houses[i].house_id,
                label = "Property: " .. houses[i].house_name
            }
        end
    end
    
    return myHouses
end
```


# Clothing

By default, the script works out-of-the-box with `illenium-appearance`, `qb-clothing`, and native `esx_skin`. If you are using a different custom clothing/appearance resource, you can integrate it easily.

There are two main areas to check: the **Custom Client File** and your **Framework Bridge**.

### 1. Applying Clothes to Ped Lineups

When the cinematic camera pans across your characters, the script needs to know how to apply their saved skin data to the ped models.

Open `custom/client.lua` and locate:

```lua
function Custom_ApplyPlayerClothing(ped, skinData)
    -- By default, it defers to the bridge file
    Bridge.Client.ApplyPlayerClothing(ped, skinData)
end
```

To modify how clothes are applied, open your respective framework bridge (e.g., `bridge/client/qbcore.lua` or `esx.lua`) and find `Bridge.Client.ApplyPlayerClothing`:

```lua
Bridge.Client.ApplyPlayerClothing = function(ped, skinData)
    -- Add your custom export or event here. Example for a custom appearance script:
    -- exports['my_custom_appearance']:setPedAppearance(ped, skinData)
    
    -- Default illenium-appearance implementation:
    if GetResourceState('illenium-appearance') == 'started' then
        exports['illenium-appearance']:setPedAppearance(ped, skinData)
    else
        TriggerEvent('qb-clothing:client:loadPlayerClothing', skinData, ped)
    end
end
```

### 2. Triggering the Character Creator

When a player finishes creating a **new character** in the UI, they drop from the sky and need to be presented with the clothing menu to create their face/outfit.

Open your respective framework bridge (e.g., `bridge/client/qbcore.lua`) and locate `Bridge.Client.LoadSkin`:

```lua
Bridge.Client.LoadSkin = function(isNewChar)
    if isNewChar then
        -- Replace this with your custom clothing creator event
        -- Example: TriggerEvent('my_custom_appearance:client:openCharacterCreator')
        TriggerEvent('qb-clothes:client:CreateFirstCharacter')
    end
end
```


# Weather & Time

To ensure the cinematic cameras, lighting, and skies look perfect during the character selection phase, **ak47\_multicharacter** forces the weather to `CLEAR` and time to `00:00` (Midnight) or whatever is set by your lighting config.

To prevent your server's time/weather sync script from fighting with the multicharacter screen (which causes screen flickering), you need to pause your sync script.

The script natively pauses `cd_easytime` and `qb-weathersync`. If you use something else (like `vSync` or a custom script), follow the steps below.

### Modifying `custom/client.lua`

Open `custom/client.lua` and locate the `Custom_StartTimeLoop()` and `Custom_StopTimeLoop()` functions.

#### 1. Pausing Sync (Start Time Loop)

Add an event trigger to pause your specific weather script.

```lua
function Custom_StartTimeLoop()
    if not timeLoop then
        timeLoop = true
        
        -- Default Integrations
        if GetResourceState('cd_easytime') == 'started' then
            TriggerEvent('cd_easytime:PauseSync', true, 1)
        else
            if GetResourceState('qb-weathersync') == 'started' then
                TriggerEvent('qb-weathersync:client:DisableSync')
            end
            
            -- ADD YOUR CUSTOM PAUSE EVENT HERE
            -- TriggerEvent('my_weathersync:pause', true)
            
            -- Built-in Override Loop
            CreateThread(function()
                while timeLoop do
                    Wait(1)
                    NetworkOverrideClockTime(1, 0, 0)
                    ClearOverrideWeather()
                    ClearWeatherTypePersist()
                    SetWeatherTypePersist('CLEAR')
                    SetWeatherTypeNow('CLEAR')
                    SetWeatherTypeNowPersist('CLEAR')
                end
            end)
        end
    end
end
```

#### 2. Resuming Sync (Stop Time Loop)

Add an event trigger to resume your weather script once the player has spawned into the world.

```lua
function Custom_StopTimeLoop()
    if timeLoop then
        timeLoop = false
        
        -- Default Integrations
        TriggerEvent('qb-weathersync:client:EnableSync')
        TriggerEvent('cd_easytime:PauseSync', false)
        
        -- ADD YOUR CUSTOM RESUME EVENT HERE
        -- TriggerEvent('my_weathersync:pause', false)
    end
end
```


# ak47\_drugmanagerv3

{% embed url="<https://youtu.be/8UtFqq5HKzo>" %}

### 🔥 Key Features

Designed to improve roleplay immersion and economy balancing:

#### 🖥️ In-Game Studio & Real-Time Management

* **Live In-Game Creator:** Built-in modern React HUD UI studio (`/drugmanager`) allows server administrators to create, position, edit, and delete drug harvest fields, processing labs, collection zones, NPC selling zones, corner boys, shops, custom usable effects, handcraft recipes, and props without ever restarting the resource or editing config files.
* **Interactive Prop Placer:** Place, rotate, and align laboratory tables, field plants, chemistry sets, and props directly in the world with precision controls.

#### 🌿 Dynamic Drug Fields & Lab Processing

* **Interactive Harvest Fields:** Place custom harvestable crops (Coca plants, Weed bushes, Opium poppy flowers, Psilocybin mushrooms, Ergot, etc.) with custom respawn times, radius limits, and harvest animations.
* **Specialized Lab Minigames:** Advanced processing stations with specialized minigames (`packaging`, `chemical`, `injection`, `brewing`) simulating chemical synthesis, brick pressing, and pouch packaging.
* **Hazard System:** Configurable lab hazard chances including catastrophic chemical explosions, fires, and player damage upon minigame failure.

#### 🤖 Autonomous Corner Boys & Street Dealing

* **NPC Corner Boys:** Deploy autonomous NPC dealers anywhere on map corners. Players can supply inventory, set custom prices, and collect passive earnings over time.
* **Street Corner Dealing:** Immersive NPC wander/corner selling system (`/startsell` & `/stopsell`) with customer ped negotiation, rejection chances, robbery attempts, and police dispatch triggers.
* **Flexible Currencies:** Configure payouts in Cash, Bank, Black Money, or Marked Bills (with metadata dollar worth support).

#### 💊 In-Depth Consumption & Visual Effects

* **Multiple Consumption Methods:** Ingest narcotics through joint/spliff smoking, bongs with realistic sound effects, syringes/shots, nasal rails, double-cup lean drinking, or swallowing pills/tabs.
* **Dynamic Screen Shaders:** Realistic GTA timecycle modifier shaders (Dax Acid Trip, Stoned Aliens, Barry Smoked Haze, Trevor Rage Redmist, Deadman Overdose, Night/Thermal vision) with motion blur, screen shake, and custom walk styles.
* **Impaired Vehicle Handling:** Simulates driving under the influence with steering drift, random forces, and loss of wheel grip.

#### 🧬 Addiction, Tolerance & Medical Detox

* **Addiction & Tolerance Tracking:** Player usage builds up drug-specific tolerance and addiction levels saved securely to the database.
* **Withdrawal Symptoms:** High addiction triggers withdrawal consequences including vomiting, sudden passouts, screen shaking, and gradual health loss.
* **Medical Detox & Antidotes:** Players can treat addictions using specialized pharmaceutical meds (Acamprosate, Lofexidine) or revive overdosing players with antidote shots.
* **Police Drug Test Kit:** Law enforcement can use interactive field drug test kits to detect active narcotics in suspects' bloodstreams.

#### 🛠️ Technical Excellence & Integrations

* **Multi-Framework Support:** Seamless out-of-the-box compatibility with ESX, QBCore, and Qbox powered by `ak47_lib`.
* **12+ Dispatch Integrations:** Automatic support for `qs-dispatch`, `ps-dispatch`, `cd_dispatch`, `core_dispatch`, `rcore_dispatch`, `op-dispatch`, `aty_dispatch`, `origen_police`, `tgiann-dispatch`, `tk_dispatch`, `wf-alerts`, `lb-phone`, and standalone alerts.
* **Target & Inventory Integration:** Fully integrated with `ox_target`, `qb-target`, `qtarget`, `ox_inventory`, `qb-inventory`, `qs-inventory`, and more.
* **Highly Secure & Optimized:** Server-side exploit prevention, distance validation, anti-trigger abuse, and standalone automated V2-to-V3 database migration (`/migratedrugsv2`).

***

### 💻 Framework:

* ESX, QB, QBX

***

### ⚙️ Dependencies:

* ak47\_lib: <https://github.com/MenanAk47/ak47\\_lib/releases/latest>

***

### ⚙️ How To Install:

* Download `ak47_drugmanagerv3` from your cfx portal.
* Add the script in your resources folder.
* Import database.sql into your database (or run /migratedrugsv2 if upgrading from V2).
* Add items based on your framework instruction.
* Add item images into inventory.
* Start the script in your `server.cfg` below framework files and ak47\_lib.
* Make sure you have all dependencies.
* Restart the server.

***

### 🔗 Links & Support:

* **🛒 Buy Now (Tebex):** <https://menanak47.tebex.io/package/7644695>
* **💬 Discord Support:** <https://discord.gg/menanak47>

***

### 📝 Note:

* We don't support custom framework. Make sure you are using official framework update.
* Renaming the script is not allowed. It may break the functionalities.
* Don't upload the script with FileZilla, Use Winscp if you are using FTP for file uploading


# Commands

Commands can be customized in \`configs/config.lua\` under \`Config.Commands\`.

### 🎮 Gameplay Commands

#### `/startsell`

Starts street-level NPC drug selling in an active PolyZone (or anywhere if `Config.SellAnyWhere = true`).

**Syntax**

```bash
/startsell
```

***

#### `/stopsell`

Cancels and stops active street-level NPC drug selling.

**Syntax**

```bash
/stopsell
```

***

### 👑 Admin Commands

#### `/drugmanager`

Opens the Drug Manager Studio 3D UI for managing territories, labs, plantations, dealers, and laboratory props.

**Syntax**

```bash
/drugmanager
```

***

#### `/removeaddiction`

Clears drug addiction levels and tolerance values.

**Syntax**

```bash
/removeaddiction [player_id]
```

**Parameters**

| Parameter   | Type     | Required                                | Description                                                     |
| ----------- | -------- | --------------------------------------- | --------------------------------------------------------------- |
| `player_id` | `number` | Optional (In-Game) / Required (Console) | Target player's server ID. Defaults to self if omitted in-game. |

**Examples**

```bash
/removeaddiction       # Clears addiction and tolerance for yourself
/removeaddiction 3     # Clears addiction and tolerance for player ID 3
removeaddiction 3      # Server console execution for player ID 3
```

***

#### `/removeoverdose`

Clears active drug overdose, stops health loss, and removes visual shaders and movement impairments.

**Syntax**

```bash
/removeoverdose [player_id]
```

**Parameters**

| Parameter   | Type     | Required                                | Description                                                     |
| ----------- | -------- | --------------------------------------- | --------------------------------------------------------------- |
| `player_id` | `number` | Optional (In-Game) / Required (Console) | Target player's server ID. Defaults to self if omitted in-game. |

**Examples**

```bash
/removeoverdose        # Clears overdose for yourself
/removeoverdose 3      # Clears overdose for player ID 3
removeoverdose 3       # Server console execution for player ID 3
```

***

#### `/cleardrugs`

Clears **all** drug addictions, tolerances, active overdoses, and screen shaders in a single action.

**Syntax**

```bash
/cleardrugs [player_id]
```

**Parameters**

| Parameter   | Type     | Required                                | Description                                                     |
| ----------- | -------- | --------------------------------------- | --------------------------------------------------------------- |
| `player_id` | `number` | Optional (In-Game) / Required (Console) | Target player's server ID. Defaults to self if omitted in-game. |

**Examples**

```bash
/cleardrugs            # Clears all drug effects & addiction for yourself
/cleardrugs 3          # Clears all drug effects & addiction for player ID 3
cleardrugs 3           # Server console execution for player ID 3
```

***

#### `/migratedrugsv2`

Manually triggers migration of legacy V2 database data into V3 structures.

**Syntax**

```bash
/migratedrugsv2
```


# Events


# Client

Client lifecycle events located in \`custom/client/events.lua\`.   Listen with \`AddEventHandler\`.

#### `ak47_drugmanagerv3:onNPCSell`

Fires when the local player successfully sells drugs to an NPC.

**Parameters**

| Parameter  | Type     | Description                                  |
| ---------- | -------- | -------------------------------------------- |
| `zoneData` | `table`  | Zone configuration table or default settings |
| `item`     | `string` | Item name sold                               |
| `amount`   | `number` | Quantity sold                                |
| `price`    | `number` | Total price received                         |

**Example**

```lua
AddEventHandler('ak47_drugmanagerv3:onNPCSell', function(zoneData, item, amount, price)
    print(('Sold %s x %s for $%s'):format(amount, item, price))
end)
```

***

#### `ak47_drugmanagerv3:onNPCRobbed`

Fires when the local player gets robbed by an NPC during drug selling.

**Parameters**

| Parameter | Type     | Description           |
| --------- | -------- | --------------------- |
| `item`    | `string` | Stolen drug item name |
| `amount`  | `number` | Quantity stolen       |

**Example**

```lua
AddEventHandler('ak47_drugmanagerv3:onNPCRobbed', function(item, amount)
    print(('Robbed of %s x %s!'):format(amount, item))
end)
```

***

#### `ak47_drugmanagerv3:onDealerSell`

Fires when the player sells items to a dealer (Shop).

**Parameters**

| Parameter  | Type     | Description                |
| ---------- | -------- | -------------------------- |
| `shopData` | `table`  | Dealer configuration table |
| `item`     | `string` | Item name sold             |
| `amount`   | `number` | Quantity sold              |
| `price`    | `number` | Total money received       |

**Example**

```lua
AddEventHandler('ak47_drugmanagerv3:onDealerSell', function(shopData, item, amount, price)
    print(('Sold %s x %s to %s for $%s'):format(amount, item, shopData.name, price))
end)
```

***

#### `ak47_drugmanagerv3:onDealerBuy`

Fires when the player buys items from a dealer (Shop).

**Parameters**

| Parameter  | Type     | Description                |
| ---------- | -------- | -------------------------- |
| `shopData` | `table`  | Dealer configuration table |
| `item`     | `string` | Item name bought           |
| `amount`   | `number` | Quantity bought            |
| `price`    | `number` | Total money paid           |

***

#### `ak47_drugmanagerv3:onCornerboyStock`

Fires when the player stocks drugs into a cornerboy.

**Parameters**

| Parameter       | Type     | Description                   |
| --------------- | -------- | ----------------------------- |
| `cornerboyData` | `table`  | Cornerboy configuration table |
| `item`          | `string` | Item name stocked             |
| `amount`        | `number` | Quantity stocked              |

***

#### `ak47_drugmanagerv3:onCornerboyTakeProfit`

Fires when the player collects profits from a cornerboy.

**Parameters**

| Parameter       | Type     | Description                                 |
| --------------- | -------- | ------------------------------------------- |
| `cornerboyData` | `table`  | Cornerboy configuration table               |
| `amount`        | `number` | Profit amount received                      |
| `currency`      | `string` | Currency type (`cash`, `black_money`, etc.) |

***

#### `ak47_drugmanagerv3:onFieldHarvestStart`

Fires when the player starts the harvest animation at a field plant.

**Parameters**

| Parameter   | Type     | Description               |
| ----------- | -------- | ------------------------- |
| `fieldData` | `table`  | Field configuration table |
| `plantId`   | `number` | Plant index in field      |

***

#### `ak47_drugmanagerv3:onFieldHarvestComplete`

Fires when the player completes harvesting a plant.

**Parameters**

| Parameter   | Type     | Description               |
| ----------- | -------- | ------------------------- |
| `fieldData` | `table`  | Field configuration table |
| `plantId`   | `number` | Plant index in field      |
| `item`      | `string` | Crop item name harvested  |
| `amount`    | `number` | Quantity harvested        |

***

#### `ak47_drugmanagerv3:onCollectStart`

Fires when the player starts collecting from a collection zone.

**Parameters**

| Parameter     | Type    | Description                         |
| ------------- | ------- | ----------------------------------- |
| `collectData` | `table` | Collection zone configuration table |

***

#### `ak47_drugmanagerv3:onCollect`

Fires when the player collects an item from a collection zone.

**Parameters**

| Parameter     | Type     | Description                         |
| ------------- | -------- | ----------------------------------- |
| `collectData` | `table`  | Collection zone configuration table |
| `item`        | `string` | Item name collected                 |
| `amount`      | `number` | Quantity collected                  |

***

#### `ak47_drugmanagerv3:onProcessStart`

Fires when the player starts processing in a laboratory zone.

**Parameters**

| Parameter     | Type    | Description                      |
| ------------- | ------- | -------------------------------- |
| `processData` | `table` | Process zone configuration table |

***

#### `ak47_drugmanagerv3:onProcess`

Fires when the player processes items in a laboratory zone.

**Parameters**

| Parameter     | Type     | Description                      |
| ------------- | -------- | -------------------------------- |
| `processData` | `table`  | Process zone configuration table |
| `item`        | `string` | Produced item name               |
| `amount`      | `number` | Quantity produced                |

***

#### `ak47_drugmanagerv3:onHandcraftStart`

Fires when the player starts handcrafting an item.

**Parameters**

| Parameter   | Type    | Description            |
| ----------- | ------- | ---------------------- |
| `craftData` | `table` | Handcraft recipe table |

***

#### `ak47_drugmanagerv3:onHandcraft`

Fires when the player completes handcrafting an item.

**Parameters**

| Parameter      | Type     | Description            |
| -------------- | -------- | ---------------------- |
| `craftData`    | `table`  | Handcraft recipe table |
| `rewardItem`   | `string` | Produced item name     |
| `rewardAmount` | `number` | Quantity produced      |

***

#### `ak47_drugmanagerv3:onDrugUsed`

Fires when the local player consumes a usable drug.

**Parameters**

| Parameter  | Type     | Description                          |
| ---------- | -------- | ------------------------------------ |
| `item`     | `string` | Item name consumed                   |
| `drugData` | `table`  | Drug configuration and effects table |

***

#### `ak47_drugmanagerv3:onOverdose`

Fires when the player begins experiencing an overdose.

**Parameters**

| Parameter | Type    | Description                                          |
| --------- | ------- | ---------------------------------------------------- |
| `details` | `table` | `{ same = boolean, mixed = boolean, drugs = table }` |

***

#### `ak47_drugmanagerv3:onAntidoteUsed`

Fires when the player uses an antidote injection.


# Server

Server lifecycle events located in \`custom/server/events.lua\`.   Listen with \`AddEventHandler\`.

#### `ak47_drugmanagerv3:onNPCSell`

Fires when a player sells drugs to an NPC.

**Parameters**

| Parameter  | Type     | Description                                 |
| ---------- | -------- | ------------------------------------------- |
| `source`   | `number` | Player server ID                            |
| `zoneData` | `table`  | Zone configuration table or default setting |
| `item`     | `string` | Item name sold                              |
| `amount`   | `number` | Quantity sold                               |
| `price`    | `number` | Total price earned                          |
| `currency` | `string` | Currency type (`cash`, `black_money`, etc.) |

**Example**

```lua
AddEventHandler('ak47_drugmanagerv3:onNPCSell', function(source, zoneData, item, amount, price, currency)
    -- Example: increase rcore_gangs loyalty
    TriggerEvent("rcore_gangs:server:increase_loyalty", source, "DRUGS", 1.0)
end)
```

***

#### `ak47_drugmanagerv3:onNPCRobbed`

Fires when a player gets robbed by an NPC.

**Parameters**

| Parameter | Type     | Description           |
| --------- | -------- | --------------------- |
| `source`  | `number` | Player server ID      |
| `item`    | `string` | Stolen drug item name |
| `amount`  | `number` | Quantity stolen       |

***

#### `ak47_drugmanagerv3:onDealerSell`

Fires when a player sells items to a dealer (Shop).

**Parameters**

| Parameter  | Type     | Description                |
| ---------- | -------- | -------------------------- |
| `source`   | `number` | Player server ID           |
| `shopData` | `table`  | Dealer configuration table |
| `item`     | `string` | Item name sold             |
| `amount`   | `number` | Quantity sold              |
| `price`    | `number` | Total price earned         |
| `currency` | `string` | Currency type              |

***

#### `ak47_drugmanagerv3:onDealerBuy`

Fires when a player buys items from a dealer (Shop).

**Parameters**

| Parameter  | Type     | Description                |
| ---------- | -------- | -------------------------- |
| `source`   | `number` | Player server ID           |
| `shopData` | `table`  | Dealer configuration table |
| `item`     | `string` | Item name bought           |
| `amount`   | `number` | Quantity bought            |
| `price`    | `number` | Total price paid           |
| `currency` | `string` | Currency type              |

***

#### `ak47_drugmanagerv3:onCornerboyStock`

Fires when a player stocks drugs into a cornerboy.

**Parameters**

| Parameter       | Type     | Description                   |
| --------------- | -------- | ----------------------------- |
| `source`        | `number` | Player server ID              |
| `cornerboyData` | `table`  | Cornerboy configuration table |
| `item`          | `string` | Item name stocked             |
| `amount`        | `number` | Quantity stocked              |

***

#### `ak47_drugmanagerv3:onCornerboyTakeProfit`

Fires when a player collects profits from a cornerboy.

**Parameters**

| Parameter       | Type     | Description                   |
| --------------- | -------- | ----------------------------- |
| `source`        | `number` | Player server ID              |
| `cornerboyData` | `table`  | Cornerboy configuration table |
| `amount`        | `number` | Profit amount received        |
| `currency`      | `string` | Currency type                 |

***

#### `ak47_drugmanagerv3:onCornerboySale`

Fires during automated background sales by a cornerboy.

**Parameters**

| Parameter       | Type     | Description                   |
| --------------- | -------- | ----------------------------- |
| `uid`           | `string` | Cornerboy unique ID           |
| `cornerboyData` | `table`  | Cornerboy configuration table |
| `item`          | `string` | Item name sold                |
| `amount`        | `number` | Quantity sold                 |
| `earned`        | `number` | Money earned                  |

***

#### `ak47_drugmanagerv3:onFieldHarvestComplete`

Fires when a player successfully harvests a plant in a drug field.

**Parameters**

| Parameter   | Type     | Description               |
| ----------- | -------- | ------------------------- |
| `source`    | `number` | Player server ID          |
| `fieldData` | `table`  | Field configuration table |
| `plantId`   | `number` | Plant index in field      |
| `item`      | `string` | Crop item name harvested  |
| `amount`    | `number` | Quantity harvested        |

***

#### `ak47_drugmanagerv3:onCollect`

Fires when a player collects an item from a collection zone.

**Parameters**

| Parameter     | Type     | Description                         |
| ------------- | -------- | ----------------------------------- |
| `source`      | `number` | Player server ID                    |
| `collectData` | `table`  | Collection zone configuration table |
| `item`        | `string` | Item name collected                 |
| `amount`      | `number` | Quantity collected                  |

***

#### `ak47_drugmanagerv3:onProcess`

Fires when a player processes an item in a laboratory zone.

**Parameters**

| Parameter     | Type     | Description                      |
| ------------- | -------- | -------------------------------- |
| `source`      | `number` | Player server ID                 |
| `processData` | `table`  | Process zone configuration table |
| `item`        | `string` | Produced item name               |
| `amount`      | `number` | Quantity produced                |

***

#### `ak47_drugmanagerv3:onHandcraft`

Fires when a player finishes handcrafting an item.

**Parameters**

| Parameter      | Type     | Description            |
| -------------- | -------- | ---------------------- |
| `source`       | `number` | Player server ID       |
| `craftData`    | `table`  | Handcraft recipe table |
| `rewardItem`   | `string` | Produced item name     |
| `rewardAmount` | `number` | Quantity produced      |

***

#### `ak47_drugmanagerv3:onDrugUsed`

Fires when a player consumes a usable drug.

**Parameters**

| Parameter  | Type     | Description                         |
| ---------- | -------- | ----------------------------------- |
| `source`   | `number` | Player server ID                    |
| `item`     | `string` | Drug item name                      |
| `drugData` | `table`  | Drug configuration and effect table |

***

#### `ak47_drugmanagerv3:onOverdose`

Fires when a player experiences an overdose.

**Parameters**

| Parameter | Type     | Description                                          |
| --------- | -------- | ---------------------------------------------------- |
| `source`  | `number` | Player server ID                                     |
| `details` | `table`  | `{ same = boolean, mixed = boolean, drugs = table }` |

***

#### `ak47_drugmanagerv3:onAntidoteUsed`

Fires when a player uses an antidote.

**Parameters**

| Parameter | Type     | Description      |
| --------- | -------- | ---------------- |
| `source`  | `number` | Player server ID |

***

#### `ak47_drugmanagerv3:onDrugTestKitUsed`

Fires when a player performs a drug test on another player.

**Parameters**

| Parameter      | Type     | Description              |
| -------------- | -------- | ------------------------ |
| `source`       | `number` | Testing player server ID |
| `targetSource` | `number` | Tested player server ID  |


# Validate


# Client

Synchronous client-side decision functions located in \`custom/client/validate.lua\`.   Return \`true\` to allow the action or \`false\` to block it.

#### `CanSellNPC`

Validates before a player starts or initiates selling to an NPC.

**Signature**

```lua
CanSellNPC(zoneData) -> boolean
```

**Data Structure (`zoneData`)**

```lua
{
    uid = "zone_1", -- string or "anywhere"
    setting = {
        name = "Downtown",
        currency = "cash",
        coprequired = 1,
        robchance = 10,
        rejectchance = 15
    }
}
```

**Example**

```lua
CanSellNPC = function(zoneData)
    if IsPedInAnyVehicle(PlayerPedId(), false) then
        Lib47.Notify('You cannot sell drugs from inside a vehicle!', 'error')
        return false
    end
    return true
end
```

***

#### `CanSellDealer`

Validates before a player opens a dealer menu or sells items to a dealer.

**Signature**

```lua
CanSellDealer(shopData) -> boolean
```

**Data Structure (`shopData`)**

```lua
{
    uid = "dealer_1",
    name = "Downtown Plug",
    shoptype = "dealer",
    defaultcurrency = "cash",
    items = {
        ["coke_pouch"] = { name = "coke_pouch", label = "Coke Pouch", sellprice = 300, buyprice = 500 }
    }
}
```

**Example**

```lua
CanSellDealer = function(shopData)
    return true
end
```

***

#### `CanBuyDealer`

Validates before a player purchases items from a dealer.

**Signature**

```lua
CanBuyDealer(shopData, item, amount) -> boolean
```

**Example**

```lua
CanBuyDealer = function(shopData, item, amount)
    return true
end
```

***

#### `CanAccessCornerboy`

Validates before a player opens a cornerboy ped interaction menu.

**Signature**

```lua
CanAccessCornerboy(cornerboyData) -> boolean
```

**Data Structure (`cornerboyData`)**

```lua
{
    uid = "corner_1",
    position = vector3(x, y, z),
    setting = {
        name = "Grove Corner",
        maxstock = 100,
        sellinterval = 5,
        profitrate = 10,
        currency = "cash"
    }
}
```

**Example**

```lua
CanAccessCornerboy = function(cornerboyData)
    return true
end
```

***

#### `CanStockCornerboy`

Validates before a player stocks drugs into a cornerboy.

**Signature**

```lua
CanStockCornerboy(cornerboyData, item, amount) -> boolean
```

***

#### `CanHarvestField`

Validates before a player begins harvesting a plant in a drug field.

**Signature**

```lua
CanHarvestField(fieldData, plantId) -> boolean
```

**Data Structure (`fieldData`)**

```lua
{
    uid = "field_weed",
    name = "Weed Field",
    item = { name = "coca_leaf", label = "Coca Leaf", minamount = 1, maxamount = 2 },
    radius = 15.0,
    delay = 3
}
```

**Example**

```lua
CanHarvestField = function(fieldData, plantId)
    if IsPedInAnyVehicle(PlayerPedId(), false) then
        return false
    end
    return true
end
```

***

#### `CanCollect`

Validates before a player collects from a collection zone.

**Signature**

```lua
CanCollect(collectData) -> boolean
```

**Data Structure (`collectData`)**

```lua
{
    uid = "collect_chem",
    setting = {
        item = { name = "chemicals", label = "Chemicals", amount = 1 },
        delay = 3,
        minigame = "none"
    }
}
```

***

#### `CanProcess`

Validates before a player starts processing in a lab zone.

**Signature**

```lua
CanProcess(processData) -> boolean
```

**Data Structure (`processData`)**

```lua
{
    uid = "lab_meth",
    setting = {
        item = { name = "meth_pouch", label = "Meth Pouch", amount = 1 },
        required = {
            ["chemicals"] = { name = "chemicals", label = "Chemicals", amount = 2 }
        },
        delay = 4,
        minigame = "mixing"
    }
}
```

***

#### `CanHandcraft`

Validates before a player starts handcrafting an item.

**Signature**

```lua
CanHandcraft(craftData) -> boolean
```

**Data Structure (`craftData`)**

```lua
{
    onuseitem = "coca_leaf",
    reward = { name = "coke_pouch", label = "Coke Pouch", amount = 1 },
    required = {
        ["bakingsoda"] = { name = "bakingsoda", label = "Baking Soda", amount = 1 }
    }
}
```

***

#### `CanUseDrug`

Validates before a player consumes a drug.

**Signature**

```lua
CanUseDrug(item, drugData) -> boolean
```

**Data Structure (`drugData`)**

```lua
{
    name = "coke_pouch",
    label = "Coke Pouch",
    usetype = "noseinhale",
    effectduration = 60,
    addhealth = 25,
    runspeed = 1.2
}
```

**Example**

```lua
CanUseDrug = function(item, drugData)
    if IsPedInAnyVehicle(PlayerPedId(), false) and drugData.usetype == 'bong' then
        Lib47.Notify('You cannot use a bong while driving!', 'error')
        return false
    end
    return true
end
```




---

[Next Page](/llms-full.txt/1)

