# Introduction

Official documentation for BOII | Development resources and projects.\
This site is a work in progress — some content may be incomplete or subject to change.\
If you spot any issues or missing info, let us know on [**Discord**](https://discord.gg/MUckUyS5Kq).

## Stay Connected

To keep up with everything BOII, make sure to join our [**Discord**](https://discord.gg/MUckUyS5Kq).\
For support requests, please note our hours below.

**Support Hours:**\
Our support server is active <mark style="color:red;">**Monday to Friday, from 10am to 10pm GMT**</mark>.\
We aim to respond within 1 business day, though in rare cases it may take up to five. \
We're human too — thanks for your patience.

***

## Our Team

BOII Development is currently a one-person operation — from code to documentation.\
Despite being solo, every effort goes into delivering clean, reliable, and production-ready tools.\
If you find the work useful, support is always appreciated.

<table data-view="cards"><thead><tr><th data-type="content-ref"></th><th data-type="content-ref"></th><th data-hidden></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-type="content-ref"></th></tr></thead><tbody><tr><td><a href="https://github.com/CaseIRL">https://github.com/CaseIRL</a></td><td><a href="https://ko-fi.com/case">https://ko-fi.com/case</a></td><td></td><td><a href="/files/ohFhEqGiCN3mAZN8qQ4M">/files/ohFhEqGiCN3mAZN8qQ4M</a></td><td><a href="https://github.com/CaseIRL">https://github.com/CaseIRL</a></td><td></td></tr></tbody></table>

***

## Support BOII Development

Many BOII Development resources are released for **free** on [**GitHub**](https://github.com/boiidevelopment). \
These take **significant** time to create, maintain, and support if you’d like to show appreciation, you can support us through the options below.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Ko-Fi</strong></td><td><a href="/files/VZ3zGooY8vmlXgegUbme">/files/VZ3zGooY8vmlXgegUbme</a></td><td><a href="https://ko-fi.com/boiidevelopment">https://ko-fi.com/boiidevelopment</a></td></tr><tr><td><strong>Buy Me A Coffee</strong></td><td><a href="/files/Nn8derPohnfb0R5OsNKj">/files/Nn8derPohnfb0R5OsNKj</a></td><td><a href="https://coff.ee/boiidevelopment">https://coff.ee/boiidevelopment</a></td></tr></tbody></table>

***

## Tebex Creator Codes

We partner with trusted creators through **Tebex Creator Codes** — a system that shares a small portion of purchases between creators.

If you're buying from a BOII partner, using our creator code gives us a small cut **and** gives you a discount.

You can apply creator codes during checkout on Tebex under **"Support A Creator."**

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>LUSTY94 SCRIPTS</strong></td><td>Code: boii10<br>Discount: 10%</td><td>Discount: 10%</td><td><a href="/files/nKCnGDaUO4SVd8UfrTyB">/files/nKCnGDaUO4SVd8UfrTyB</a></td><td></td><td></td></tr><tr><td><strong>OOSAYEROO SCRIPTS</strong></td><td>Code: boii10<br>Discount: 10%</td><td>Discount 10%</td><td><a href="/files/suib98S409uiNdXIQAB4">/files/suib98S409uiNdXIQAB4</a></td><td></td><td></td></tr></tbody></table>


# BDTK

{% hint style="danger" %}
**THIS SECTION IS A WORK IN PROGRESS BDTK WILL REPLACE BOII\_UTILS EVENTUALY.**\
**EVERYTHING WILL BE DOCUMENTED FIRST.**
{% endhint %}

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

***

## What is BDTK?

Normally? **BOII Development Tool Kit**\
When it works? **Big Damn Time-saving Kit**\
When it doesn’t? **Broken Dumbass Time Killer**

BDTK is the next evolution of `boii_utils` - rewritten, reorganized, and refined for modern FiveM scripting.

It’s still modular, still framework-agnostic, but now with tighter internals, smarter structure, and a more focused set of tools. From framework bridging to utility wrappers, animation helpers to UI fallbacks - it’s built to slot into any dev stack and cut the busywork out of your workflow.

No bloat. No gatekeeping. No nonsense.\
Just dependable functions, clean patterns, and a codebase that stays out of your way.

***

## Who Is It For?

* **Framework developers** who need clean, reusable utility modules for base functionality.
* **Script authors** who want their releases to support multiple frameworks with minimal effort.
* **Solo scripters** tired of rewriting the same junk over and over again.
* **Teams** looking for a shared library of tools to streamline development across projects.

Whether you're writing scripts or an entire framework, BDTK helps cut the fat and scale the logic.

***

## Why Should I Use It?

* **Framework Bridge** – Built to abstract and unify major frameworks (`qb`, `esx`, `ox`, `nd`, `boii`, etc.) so you don’t have to write double logic.
* **Modular by Default** – Use only what you need. Every module is isolated, with no hard dependencies on the rest.
* **Less Repetition** – Handles the common patterns and boring parts so you can focus on actual logic, not boilerplate.
* **Consistent Conventions** – Shared structure and naming across modules keeps your code clean and predictable.
* **Dev Time Saver** – Speeds up development with drop-in systems, helper functions, and sane defaults.

***

## What Does It Provide?

BDTK is split into multiple categories of modules:

#### Resource Bridges

* **Framework Bridge** - Currently compatible with ESX, QB-Core, QBox, Ox Core, or ND Core.
* **Notification Bridge** - Currently compatible with bduk, boii\_ui, es\_extended, okokNotify, ox\_lib, qb-core.
* **DrawText UI Bridge** - Currently compatible with boii\_ui, es\_extended, okokNotify, ox\_lib, qb-core.

#### Standalone Systems

* **Callbacks** - Full client/server callback handling without framework dependencies.
* **Commands** - Built-in permissions, Ace support, and command registration.
* **Licences** - Theory/practical tests, points, revoking—DMV-style, but smarter.
* **XP System** - Custom growth curves, XP types, and server-wide level tracking.

#### Utility Modules

* **Appearance** - Appearance, clothing, tattoos, and shared styling logic.
* **Vehicles** - Entity-safe functions for customization, storage, and behavior.
* **Items** - Usable item registry outside of any core system.
* **Methods** - Attach runtime functions to players, vehicles, or anything else.
* **Player Helpers** - Animations, props, directions, and ped-related helpers.
* **Timestamps** - Server-safe date/time utils for consistent formatting.
* **Environment** - Time, weather, seasonal effects, and sync helpers.
* **Entities** - Utility functions for managing NPCs, vehicles, and objects.
* **Profanity** - Handles profanity related filtering and replacing.
* Buckets - Routing bucket handling and static data storage.

#### Smart Libraries

* **Geometry** - Vector math, angles, zones, distance, and shape logic.
* **Maths** - Extended math with curves, clamping, interpolation, and more.
* **Strings** - Pad, slugify, wrap, and format anything text-based.
* **Tables** - Merge, clone, randomize, and clean up Lua tables.
* **Keys** - Named constants for all common input keys, with helpers.

***

## How Is It Structured?

Every module in BDTK is fully self-contained — no tangled dependencies, no weird global hacks.\
They’re designed to be loaded individually or accessed cleanly through the central `bdtk.get()` system.

BDTK is:

* **Bridge-Based** – Framework functions are abstracted and unified behind a common API.
* **Environment-Aware** – Modules work on the client, server, or both depending on context.
* **Drop-In Ready** – Use one module or all of them. It won’t break if you don’t use the full set.
* **Scalable by Design** – Built to grow with your project without becoming a mess.

***

## Quick Install

To get setup using **BDTK** follow these brief steps:

{% stepper %}
{% step %}
**Download BDTK**

Download the Latest Release of BDTK from GitHub.
{% endstep %}

{% step %}
**Add It To Your Server**

Drop `bdtk` into your server resources.
{% endstep %}

{% step %}

#### Add It To Your Server Config

Add `ensure bdtk` into your `server.cfg` make sure this line is **above** any resource requiring it.
{% endstep %}

{% step %}
**Insert The SQL**

Add the included `REQUIRED.sql` into your database, this is required for user accounts and some standalone systems.
{% endstep %}

{% step %}
**Restart Your Server**

Restart your server and BDTK will be up and running, all bridges run auto-detection and have safe fall backs if none of the supported resources are found. To change default configuration settings you can do this via `convars` for more on this read: **Configuring BDTK**
{% endstep %}
{% endstepper %}

***

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Guides</td><td><a href="/files/mXGQlzqtxWDAWC63HwSh">/files/mXGQlzqtxWDAWC63HwSh</a></td><td><a href="/pages/Z0V242UcuOSPwAiUTPfC">/pages/Z0V242UcuOSPwAiUTPfC</a></td></tr><tr><td>API Reference</td><td><a href="/files/lfc1oyGmqtwJalCePlXA">/files/lfc1oyGmqtwJalCePlXA</a></td><td><a href="/pages/lDrgX8pB2w41qtRbjGMA">/pages/lDrgX8pB2w41qtRbjGMA</a></td></tr></tbody></table>


# Guides


# API


# Bridges


# Framework


# Notifications


# DrawText UI


# Modules


# Appearance

## Client

### get\_clothing\_and\_prop\_values

Returns maximum values for clothing and prop UI sliders based on ped model.

#### Parameters

* sex: `string`

#### Returns

* `table`

```lua
bdtk.get_clothing_and_prop_values(sex)
```

***

### set\_ped\_appearance

Applies genetics, hair, overlays, clothes, and tattoos to the player's ped.

#### Parameters

* player: `number`
* data: `table`

#### Returns

* `void`

```lua
bdtk.set_ped_appearance(player, data)
```

***

### update\_ped\_appearance

Updates the internal `ped_styles` table and re-applies the appearance.

#### Parameters

* sex: `string`
* category: `string`
* id: `string|nil`
* value: `any`

#### Returns

* `void`

```lua
bdtk.update_ped_appearance(sex, category, id, value)
```

***

### change\_player\_ped

Changes the player's model based on the given sex.

#### Parameters

* sex: `string`

#### Returns

* `void`

```lua
bdtk.change_player_ped(sex)
```

***

### appearance\_rotate\_ped

Rotates the player's ped in a specific direction.

#### Parameters

* direction: `string`

#### Returns

* `void`

```lua
bdtk.appearance_rotate_ped(direction)
```

***

### load\_player\_appearance

Loads and applies a full character model including clothing and features.

#### Parameters

* data: `table`

#### Returns

* `void`

```lua
bdtk.load_player_appearance(data)
```

***

## Shared

### get\_ped\_appearence

Returns the current style data from `ped_styles` for a given sex.

#### Parameters

* sex: `string`

#### Returns

* `table`

```lua
bdtk.get_ped_appearence(sex)
```

***

### reset\_appearence\_styles

Resets the internal `ped_styles` table to its default values.

#### Returns

* `void`

```lua
bdtk.reset_appearence_styles()
```


# Buckets

{% hint style="warning" %}
**SERVER FUNCTIONS**
{% endhint %}

## bucket\_list

Stores bucket definitions for script use.\
If you need the full list for any reason you can access it this way.

```lua
bdtk.bucket_list = {
    main = {
        label = "Main World", -- label for display
        bucket = 0, -- default bucket
        mode = "strict", -- https://docs.fivem.net/natives/?_0xA0F2201F
        population_enabled = false, -- https://docs.fivem.net/natives/?_0xCE51AC2C
        player_cap = false, -- false | number: If number players max players in bucket will be capped at value.
        staff_only = false, -- e.g. { "admin", "mod" }
        vip_only = false, -- e.g. 2 = VIP level 2 required
        spawn = vector4(-268.47, -956.98, 31.22, 208.54), -- spawn point
        respawn = vector4(341.28, -1396.83, 32.51, 48.78) -- respawn point
    }
}
```

***

## get\_bucket

Returns the full bucket config for a given ID.

#### Parameters

* id: `string`

#### Returns

* `table | nil`

```lua
bdtk.get_bucket(id)
```

***

## get\_bucket\_by\_label

Returns the bucket config matching a label.

#### Parameters

* label: `string`

#### Returns

* `table | nil`

```lua
bdtk.get_bucket_by_label(label)
```

***

## get\_bucket\_spawn

Returns the spawn point for a bucket.

#### Parameters

* id: `string`

#### Returns

* `vector4 | nil`

```lua
bdtk.get_bucket_spawn(id)
```

***

## get\_bucket\_respawn

Returns the respawn point for a bucket.

#### Parameters

* id: `string`

#### Returns

* `vector4 | nil`

```lua
bdtk.get_bucket_respawn(id)
```

***

## is\_bucket\_staff\_only

Checks if a bucket is staff-only for a given role.

#### Parameters

* id: `string`
* role: `string`

#### Returns

* `boolean`

```lua
bdtk.is_bucket_staff_only(id, role)
```

***

## is\_bucket\_vip\_only

Checks if a bucket is VIP-only for a given level.

#### Parameters

* id: `string`
* vip\_level: `number`

#### Returns

* `boolean`

```lua
bdtk.is_bucket_vip_only(id, vip_level)
```

***

## apply\_bucket\_settings

Applies population and lockdown mode to all defined buckets.

```lua
bdtk.apply_bucket_settings()
```


# Callbacks

## Server

### register\_callback

Registers a server-side callback.

#### Parameters

* name: `string`
* cb: `function`

#### Returns

* `void`

```lua
bdtk.register_callback(name, cb)
```

***

## Client

### trigger\_callback

Triggers a server-side callback and handles the response.

#### Parameters

* name: `string`
* data: `table`
* cb: `function`

#### Returns

* `void`

```lua
bdtk.trigger_callback(name, data, cb)
```


# Commands

## Server

### register\_command

Registers a command with permission check and optional chat suggestions.

#### Parameters

* command: `string`
* rank: `string|table|nil`
* help: `string`
* params: `table`
* handler: `function`

```lua
bdtk.register_command(command, rank, help, params, handler)
```

***

## Client

### get\_command\_suggestions

Requests command chat suggestions for users.\
These are the tooltips that display when typing commands.

#### Returns

* `table`

```lua
bdtk.get_command_suggestions()
```


# Cooldowns

{% hint style="warning" %}
**SERVER FUNCTIONS**
{% endhint %}

## add\_cooldown

Adds a cooldown for a player or globally.

#### Parameters

* source: `number`
* cooldown\_type: `string`
* duration: `number`
* is\_global: `boolean`

```lua
bdtk.add_cooldown(source, cooldown_type, duration, is_global)
```

***

## check\_cooldown

Checks if a cooldown is active.

#### Parameters

* source: `number`
* cooldown\_type: `string`
* is\_global: `boolean`

#### Returns

* `boolean`

```lua
bdtk.check_cooldown(source, cooldown_type, is_global)
```

***

## clear\_cooldown

Clears a specific cooldown.

#### Parameters

* source: `number`
* cooldown\_type: `string`
* is\_global: `boolean`

```lua
bdtk.clear_cooldown(source, cooldown_type, is_global)
```

***

## clear\_expired\_cooldowns

Clears all expired cooldowns.

```lua
bdtk.clear_expired_cooldowns()
```

***

## clear\_resource\_cooldowns

Clears all cooldowns for a given resource.

#### Parameters

* resource: `string`

```lua
bdtk.clear_resource_cooldowns(resource)
```


# Debugging

{% hint style="warning" %}
**SHARED FUNCTIONS**
{% endhint %}

## log

Prints a formatted debug message to the console.

#### parameters

* level: `string`
* message: `string`

```lua
bdtk.log(level, message)
```

***

## wait\_for

Waits until a given function returns true or a timeout is reached.

#### parameters

* fn: `function`
* timeout: `number|nil`
* interval: `number|nil`

#### returns

* `boolean`

```lua
bdtk.wait_for(fn, timeout, interval)
```


# Entities

{% hint style="warning" %}
**CLIENT FUNCTIONS**
{% endhint %}

## get\_nearby\_entities

Finds nearby entities based on pool and position.

#### Parameters

* pool: `string`
* coords: `vector3`
* max\_distance: `number`
* filter: `function|nil`

#### Returns

* `table`

```lua
bdtk.get_nearby_entities(pool, coords, max_distance, filter)
```

***

## get\_nearby\_objects

Returns nearby object entities.

#### Parameters

* coords: `vector3`
* max\_distance: `number`

#### Returns

* `table`

```lua
bdtk.get_nearby_objects(coords, max_distance)
```

***

## get\_nearby\_peds

Returns nearby peds, excluding players.

#### Parameters

* coords: `vector3`
* max\_distance: `number`

#### Returns

* `table`

```lua
bdtk.get_nearby_peds(coords, max_distance)
```

***

## get\_nearby\_players

Returns nearby players.

#### Parameters

* coords: `vector3`
* max\_distance: `number`
* include\_player: `boolean`

#### Returns

* `table`

```lua
bdtk.get_nearby_players(coords, max_distance, include_player)
```

***

## get\_nearby\_vehicles

Returns nearby vehicles.

#### Parameters

* coords: `vector3`
* max\_distance: `number`
* include\_player\_vehicle: `boolean`

#### Returns

* `table`

```lua
bdtk.get_nearby_vehicles(coords, max_distance, include_player_vehicle)
```

***

## get\_closest\_object

Gets the closest object entity.

#### Parameters

* coords: `vector3`
* max\_distance: `number`

#### Returns

* `number`, `vector3`

```lua
bdtkbdtk.get_closest_object(coords, max_distance)
```

***

## get\_closest\_ped

Gets the closest ped entity.

#### Parameters

* coords: `vector3`
* max\_distance: `number`

#### Returns

* `number`, `vector3`

```lua
bdtk.get_closest_ped(coords, max_distance)
```

***

## get\_closest\_player

Gets the closest player entity.

#### Parameters

* coords: `vector3`
* max\_distance: `number`
* include\_player: `boolean`

#### Returns

* `number`, `vector3`

```lua
bdtk.get_closest_player(coords, max_distance, include_player)
```

***

## get\_closest\_vehicle

Gets the closest vehicle entity.

#### Parameters

* coords: `vector3`
* max\_distance: `number`
* include\_player\_vehicle: `boolean`

#### Returns

* `number`, `vector3`

```lua
bdtk.get_closest_vehicle(coords, max_distance, include_player_vehicle)
```

***

## get\_entities\_in\_front\_of\_player

Returns the entity directly in front of the player.

#### Parameters

* fov: `number`
* distance: `number`

#### Returns

* `number|nil`

```lua
bdtk.get_entities_in_front_of_player(fov, distance)
```

***

## get\_target\_ped

Gets the ped in front of the player or the closest.

#### Parameters

* player\_ped: `number`
* fov: `number`
* distance: `number`

#### Returns

* `number`, `vector3`

```lua
bdtk.get_target_ped(player_ped, fov, distance)
```


# Environment

{% hint style="warning" %}
**CLIENT FUNCTIONS**
{% endhint %}

## get\_weather\_name

Returns the weather name from a given hash.

#### Parameters

* hash: `number`

#### Returns

* `string`

```lua
bdtk.get_weather_name(hash)
```

***

## get\_game\_time

Returns current game time and formatted version.

#### Returns

* `table`

```lua
bdtk.get_game_time()
```

***

## get\_game\_date

Returns current game date and formatted version.

#### Returns

* `table`

```lua
bdtk.get_game_date()
```

***

## get\_sunrise\_sunset\_times

Returns sunrise and sunset times for given weather.

#### Parameters

* weather: `string`

#### Returns

* `table`

```lua
bdtk.get_sunrise_sunset_times(weather)
```

***

## is\_daytime

Checks if it is currently daytime.

#### Returns

* `boolean`

```lua
bdtk.is_daytime()
```

***

## is\_nighttime

Checks if it is currently nighttime.

#### Returns

* `boolean`

```lua
bdtk.is_nighttime()
```

***

## is\_midday

Checks if it is currently midday.

#### Returns

* `boolean`

```lua
bdtk.is_midday()
```

***

## get\_current\_season

Returns current season.

#### Returns

* `string`

```lua
bdtk.get_current_season()
```

***

## get\_distance\_to\_water

Returns distance from player to nearest water body.

#### Returns

* `number`

```lua
bdtk.get_distance_to_water()
```

***

## get\_zone\_scumminess

Returns the zone scumminess level.

#### Returns

* `integer`

```lua
bdtk.get_zone_scumminess()
```

***

## get\_ground\_material

Returns ground material hash at player's position.

#### Returns

* `number`

```lua
bdtk.get_ground_material()
```

***

## get\_wind\_direction

Returns wind direction as a compass value.

#### Returns

* `string`

```lua
bdtk.get_wind_direction()
```

***

## get\_altitude

Returns player's altitude above sea level.

#### Returns

* `number`

```lua
bdtk.get_altitude()
```

***

## get\_environment\_details

Returns a summary of environmental data.

#### Returns

* `table`

```lua
bdtk.get_environment_details()
```


# Geometry

{% hint style="warning" %}
**SHARED FUNCTIONS**
{% endhint %}

## distance\_2d

Calculates the distance between two 2D points.

#### Parameters

* p1: `table`
* p2: `table`

#### Returns

* `number`

```lua
bdtk.distance_2d(p1, p2)
```

***

## distance\_3d

Calculates the distance between two 3D points.

#### Parameters

* p1: `table`
* p2: `table`

#### Returns

* `number`

```lua
bdtk.distance_3d(p1, p2)
```

***

## midpoint

Returns the midpoint between two 3D points.

#### Parameters

* p1: `table`
* p2: `table`

#### Returns

* `table`

```lua
bdtk.midpoint(p1, p2)
```

***

## is\_point\_in\_rect

Determines if a point is inside a given 2D rectangle boundary.

#### Parameters

* point: `table`
* rect: `table`

#### Returns

* `boolean`

```lua
bdtk.is_point_in_rect(point, rect)
```

***

## is\_point\_in\_box

Determines if a point is inside a given 3D box boundary.

#### Parameters

* point: `table`
* box: `table`

#### Returns

* `boolean`

```lua
bdtk.is_point_in_box(point, box)
```

***

## is\_point\_on\_line\_segment

Determines if a point is on a line segment defined by two 2D points.

#### Parameters

* point: `table`
* line\_start: `table`
* line\_end: `table`

#### Returns

* `boolean`

```lua
bdtk.is_point_on_line_segment(point, line_start, line_end)
```

***

## project\_point\_on\_line

Projects a point onto a line segment defined by two 2D points.

#### Parameters

* p: `table`
* p1: `table`
* p2: `table`

#### Returns

* `table`

```lua
bdtk.project_point_on_line(p, p1, p2)
```

***

## calculate\_slope

Calculates the slope of a line given two 2D points.

#### Parameters

* p1: `table`
* p2: `table`

#### Returns

* `number`

```lua
bdtk.calculate_slope(p1, p2)
```

***

## angle\_between\_points

Returns the angle between two 2D points in degrees.

#### Parameters

* p1: `table`
* p2: `table`

#### Returns

* `number`

```lua
bdtk.angle_between_points(p1, p2)
```

***

## angle\_between\_3\_points

Calculates the angle between three 3D points (p1, p2 as center, p3).

#### Parameters

* p1: `table`
* p2: `table`
* p3: `table`

#### Returns

* `number`

```lua
bdtk.angle_between_3_points(p1, p2, p3)
```

***

## do\_circles\_intersect

Determines if two circles defined by center and radius intersect.

#### Parameters

* c1\_center: `table`
* c1\_radius: `number`
* c2\_center: `table`
* c2\_radius: `number`

#### Returns

* `boolean`

```lua
bdtk.do_circles_intersect(c1_center, c1_radius, c2_center, c2_radius)
```

***

## is\_point\_in\_circle

Determines if a point is inside a circle defined by center and radius.

#### Parameters

* point: `table`
* circle\_center: `table`
* circle\_radius: `number`

#### Returns

* `boolean`

```lua
bdtk.is_point_in_circle(point, circle_center, circle_radius)
```

***

## do\_lines\_intersect

Determines if two 2D line segments intersect.

#### Parameters

* l1\_start: `table`
* l1\_end: `table`
* l2\_start: `table`
* l2\_end: `table`

#### Returns

* `boolean`

```lua
bdtkbdtk.do_lines_intersect(l1_start, l1_end, l2_start, l2_end)
```

***

## line\_intersects\_circle

Determines if a line segment intersects a circle.

#### Parameters

* line\_start: `table`
* line\_end: `table`
* circle\_center: `table`
* circle\_radius: `number`

#### Returns

* `boolean`

```lua
bdtk.line_intersects_circle(line_start, line_end, circle_center, circle_radius)
```

***

## does\_rect\_intersect\_line

Determines if a rectangle intersects with a 2D line segment.

#### Parameters

* rect: `table`
* line\_start: `table`
* line\_end: `table`

#### Returns

* `boolean`

```lua
bdtk.does_rect_intersect_line(rect, line_start, line_end)
```

***

## closest\_point\_on\_line\_segment

Determines the closest point on a 2D line segment to a given point.

#### Parameters

* point: `table`
* line\_start: `table`
* line\_end: `table`

#### Returns

* `table`

```lua
bdtk.closest_point_on_line_segment(point, line_start, line_end)
```

***

## triangle\_area\_3d

Calculates the area of a 3D triangle given three points.

#### Parameters

* p1: `table`
* p2: `table`
* p3: `table`

#### Returns

* `number`

```lua
bdtk.triangle_area_3d(p1, p2, p3)
```

***

## is\_point\_in\_sphere

Determines if a point is inside a 3D sphere defined by center and radius.

#### Parameters

* point: `table`
* sphere\_center: `table`
* sphere\_radius: `number`

#### Returns

* `boolean`

```lua
bdtk.is_point_in_sphere(point, sphere_center, sphere_radius)
```

***

## do\_spheres\_intersect

Determines if two spheres intersect.

#### Parameters

* s1\_center: `table`
* s1\_radius: `number`
* s2\_center: `table`
* s2\_radius: `number`

#### Returns

* `boolean`

```lua
bdtk.do_spheres_intersect(s1_center, s1_radius, s2_center, s2_radius)
```

***

## is\_point\_in\_convex\_polygon

Determines if a point is inside a 2D convex polygon.

#### Parameters

* point: `table`
* polygon: `table`

#### Returns

* `boolean`

```lua
bdtk.is_point_in_convex_polygon(point, polygon)
```

***

## rotate\_point\_around\_point\_2d

Rotates a point around another point in 2D by a given angle in degrees.

#### Parameters

* point: `table`
* pivot: `table`
* angle\_degrees: `number`

#### Returns

* `table`

```lua
bdtk.rotate_point_around_point_2d(point, pivot, angle_degrees)
```

***

## distance\_point\_to\_plane

Calculates the distance from a point to a plane.

#### Parameters

* point: `table`
* plane\_point: `table`
* plane\_normal: `table`

#### Returns

* `number`

```lua
bdtk.distance_point_to_plane(point, plane_point, plane_normal)
```

***

## rotation\_to\_direction

Converts a rotation vector to a direction vector.

#### Parameters

* rotation: `table`

#### Returns

* `table`

```lua
bdtk.rotation_to_direction(rotation)
```

***

## rotate\_box

Rotates a box around a central point in 3D by a given heading.

#### Parameters

* center: `table`
* width: `number`
* length: `number`
* heading: `number`

#### Returns

* `table`

```lua
bdtk.rotate_box(center, width, length, heading)
```

***

## calculate\_rotation\_matrix

Calculates a rotation matrix from heading, pitch, and roll.

#### Parameters

* heading: `number`
* pitch: `number`
* roll: `number`

#### Returns

* `table`

```lua
geometry.calculate_rotation_matrix(heading, pitch, roll)
```

***

## translate\_point\_to\_local\_space

Translates a point to a box's local coordinate system using a rotation matrix.

#### Parameters

* point: `table`
* box\_origin: `table`
* rot\_matrix: `table`

#### Returns

* `table`

```lua
bdtk.translate_point_to_local_space(point, box_origin, rot_matrix)
```

***

## is\_point\_in\_oriented\_box

Determines if a point is inside an oriented 3D box.

#### Parameters

* point: `table`
* box: `table`

#### Returns

* `boolean`

```lua
bdtk.is_point_in_oriented_box(point, box)
```


# BDUK

{% hint style="danger" %}

### ALPHA RELEASE

The current version is an alpha release, some things may change and things will definitely be added.\
Most changes to core logic will be small if any, however in cases of larger changes be prepared to have to update your code a little.
{% endhint %}

<figure><img src="/files/2EHsHzveuUYqA23eIWaD" alt=""><figcaption></figcaption></figure>

***

## What is BDUK?

Normally? **BOII Development UI Kit**\
When it’s working so well it feels like magic? **BIG D\*CK UI KUNG-FU!**\
When it refuses to cooperate? **Buggy Dumbass Useless Kit**

Tired of learning front end UI code? \
Well now you don’t have to.

**BDUK** is a front-end toolkit built to cover all your scripting needs.\
It builds full UIs from Lua — no HTML, no CSS, no JavaScript knowledge required.\
Think of a context menu resource — but bigger.. a lot bigger.

Headers, footers, modals, cards, buttons, tooltips, themes — it’s all handled.\
Pick what you need, leave what you don’t, and build whatever the hell you can come up with.

***

## **Who’s It For?**

* People who can’t code UI — and don’t want to.
* Script authors who’d rather build actual gameplay.
* UI nerds can use it too — but this one’s built for the rest of you folks.
* You. Because you're done with context menus controlling everything.

***

## Why Bother Using It?

* **No Bloat** — Only includes what you add.
* **No Guesswork —** Modals, buttons, and tooltips just work.
* **Reusability** — Build once, use everywhere.
* **Uniformity —** You want the same uniform look, across all your scripts. &#x20;

***

## What’s Included?

**Main UI Frame:**

* **Header** **—** With slots for branding, buttons, and maybe one day a picture of your nan's cat.
* **Footer** **—** Button hints, status text, and keybind rows for people who forget controls.
* **Sidebar** **—** Optional side menu with submenu support.
* **Content** **—** Where your actual UI goes.. the meat and veg so to speak.
* **Tooltip** **—** Hover text system. Originally from my inventory UI. Now universal.

**Components:**

* **Buttons —** Generates buttons with optional modal injection built-in.
* **Cards** — Reusable building blocks. Use for stores, jobs, menus, etc.
* **Input Groups** **— I**n optional expandable containers. Use for clothing menus, vehicle customs, etc.
* **Modal** **—** Text inputs, selects, numbers, textareas, colour pickers. It’s a modal.
* **Namecards** — Stylised name-card display for character profiles with avatar, name, title, level.
* **Notify —** Small notification system for in-UI alerts.

**Extras:**

* **Themes** – One file to override fonts, colours, spacing, and general look.
* **Layouts (soon)** – Premade store, job centre, and panel setups coming when I feel like it.

***

## Quick Install

The quickest way to get setup is following the instructions below. \
For more detailed installation instructions, or configuring BDUK head here: [**Guides**](/fivem-free-resources/bduk/guides)

{% stepper %}
{% step %}

#### Download

Download the [**LATEST RELEASE**](https://github.com/boiidevelopment/bduk/releases) from GitHub&#x20;
{% endstep %}

{% step %}

#### Add The Resource

Add the `bduk` resource into your server resources.
{% endstep %}

{% step %}

#### Add To Server.cfg

Add the following line into your `server.cfg` file: `ensure bduk`
{% endstep %}

{% step %}

#### Restart / Start Resource

Restart your server or press `F8` and type `refresh; ensure bduk` and the resource will be started.&#x20;
{% endstep %}

{% step %}

#### Configuration

Configuration generally boils down to themes, you can modify the defaults or create your own. \
More on themes: [**Creating Your First Theme**](/fivem-free-resources/bduk/guides/creating-your-first-theme)
{% endstep %}
{% endstepper %}


# Guides

This section covers how to use BDUK to build and manage your UI layouts.

You'll find quick guides on:

* Creating your first UI
* Making and applying themes
* ... more to come

Start with [**Making Your First UI**](/fivem-free-resources/bduk/guides/making-your-first-ui) if you're new.

***

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Making Your First UI</strong></td><td><a href="/files/kJ1zmPXhMiBrth5ysJX8">/files/kJ1zmPXhMiBrth5ysJX8</a></td><td><a href="/pages/Jn70Y4pTISBHYzdMJ7u3">/pages/Jn70Y4pTISBHYzdMJ7u3</a></td></tr><tr><td><strong>Creating Your First Theme</strong></td><td><a href="/files/yxOzlqEdMd14gt50gMIh">/files/yxOzlqEdMd14gt50gMIh</a></td><td><a href="/pages/dK28SAEuUemECEk6PObF">/pages/dK28SAEuUemECEk6PObF</a></td></tr></tbody></table>


# Making Your First UI

This guide walks you through how to build your first UI layout using BDUK’s Lua-based config system.\
No HTML or JS needed — just declare what you want using simple tables, then call `build()`.

***

## Basic Structure

A complete UI is made up of:

* `header` – sits at the top of your UI with 3 sections (left/center/right)
* `footer` – fixed to the bottom, also with left/center/right
* `content` – contains all main pages and components
* *(optional)* `sidebar` – can be added later for tools or quick actions

***

## Step 1: Define a UI Layout

Here’s a stripped-down version to start with:

```lua
local my_ui = {
    header = {
        layout = {
            left = { justify = "flex-start" },
            center = { justify = "center" },
            right = { justify = "flex-end" },
        },
        elements = {
            left = {
                {
                    type = "group",
                    items = {
                        { type = "logo", image = "bd_100.png" },
                        { type = "text", title = "My UI", subtitle = "Made with BDUK" }
                    }
                }
            },
            center = { { type = "tabs" } },
            right = { { type = "namecard", avatar = "avatar_placeholder.jpg", name = "Player", title = "Developer", level = 1, tier = "gold" } }
        }
    },

    content = {
        pages = {
            main_page = {
                index = 1,
                title = "Main Page",
                layout = { center = 6 },
                center = {
                    type = "input_groups",
                    title = "Options",
                    id = "basic_inputs",
                    layout = { columns = 1 },
                    groups = {
                        {
                            header = "General Settings",
                            inputs = {
                                { id = "setting_1", type = "text", label = "Your Name" },
                                { id = "setting_2", type = "number", label = "Age" }
                            }
                        }
                    },
                    buttons = {
                        { id = "save", label = "Save", on_click = function(data) ... end, class = "primary" }
                    }
                }
            }
        }
    },

    footer = {
        layout = {
            left = { justify = "flex-start" },
            center = { justify = "center" },
            right = { justify = "flex-end" },
        },
        elements = {
            left = {
                {
                    type = "buttons",
                    buttons = {
                        { id = "back", label = "Back", on_click = function(data) ... end, class = "secondary" }
                    }
                }
            },
            center = { { type = "text", text = "Made with 💙 by BOII Development" } },
            right = {
                { type = "actions", actions = { { key = "E", label = "Confirm", on_keypress = function(data) ... end, }, { key = "ESC", label = "Close", on_keypress = function(data) ... end, } } }
            }
        }
    }
}
```

***

## Step 2: Show the UI

Just trigger it using:

```lua
RegisterCommand("open_my_ui", function()
    exports.bduk:build(my_ui)
end)
```

***

## Adding Components

Here’s a few things you can use inside your layout sections:

| **Type**       | **Description**                             |
| -------------- | ------------------------------------------- |
| `text`         | Shows a label or info line                  |
| `logo`         | Displays a logo image                       |
| `tabs`         | Adds tabbed navigation (auto-handled)       |
| `namecard`     | Shows a styled player profile card          |
| `buttons`      | One or more clickable buttons               |
| `input_groups` | Grouped form fields (number/text/etc.)      |
| `cards`        | Panel cards with image, info, and buttons   |
| `actions`      | Shows keys like `E` or `ESC` to guide users |

***

## Pages Explained

The `content.pages` section defines every page in your UI:

```lua
content = {
    pages = {
        my_page = {
            index = 1,
            title = "My Page",
            layout = { left = 3, center = 6, right = 3 },
            center = {
                type = "cards",
                title = "Items",
                layout = { columns = 2 },
                cards = { ... }
            }
        }
    }
}
```

#### Each page has:

* `index`: Page order
* `title`: Page label (used in tabs)
* `layout`: Grid sizes for `left`, `center`, and `right` (adds up to 12)
* `left`, `center`, `right`: Each holds a single component (e.g. `cards`, `input_groups`, etc.)

***

## Tips

* All `buttons` can include modals or datasets
* Tooltips and keybinds are built into cards via `on_hover`
* Use `dataset` fields to pass IDs, source names, or action context

***

## You’re Ready!

Try running:

```bash
/test_ui_builder
```

Or rename your config and register it under your own command to test.


# Creating Your First Theme

Themes in BDUK define the overall look and feel of your UI — including fonts, colors, borders, gradients, and effects.\
You can create your own themes by editing a simple CSS file and registering it in the system config.

***

## Step 1: Copy the Default Theme

Start with the default theme located at:

```
/ui/css/themes/default.css
```

Make a copy, give it a name like:

```
/ui/css/themes/mytheme.css
```

> **Keep the credit line at the top** — support honest development. don't be that guy...\
> Don't forget to add your own credit note below.&#x20;

***

## Step 2: Edit Your Theme

Open your new CSS file and change any of the variables inside the `body.YOUR_THEME_NAME` block.

Below is a complete list of available CSS variables you can use when customizing your theme:

| **Category**       | **Variable**                              | **Description**                              |
| ------------------ | ----------------------------------------- | -------------------------------------------- |
| **Fonts**          | `--header_font`                           | Font used in headers                         |
|                    | `--text_font`                             | Font used for regular text                   |
| **Border Radius**  | `--border_radius_outer`                   | Radius for card/panel corners                |
|                    | `--border_radius_inner`                   | Radius for inner elements like inputs        |
| **Backgrounds**    | `--background_main`                       | Main background color                        |
|                    | `--background_panel`                      | Panels/containers background                 |
|                    | `--background_hover`                      | Background when hovered                      |
|                    | `--background_overlay`                    | Overlay background for modals/tooltips       |
| **Text Colors**    | `--text_primary`                          | Main text color                              |
|                    | `--text_secondary`                        | Subtext or secondary UI text                 |
|                    | `--text_tertiary`                         | Placeholder or hint text                     |
| **Accents**        | `--accent`                                | Primary accent color                         |
|                    | `--accent2`                               | Secondary accent                             |
|                    | `--accent3`                               | Used for alerts/danger zones                 |
|                    | `--accent4`                               | Extra accent (often for icons or details)    |
| **Borders**        | `--border_dark`                           | Dark border (used for cards/panels)          |
|                    | `--border_light`                          | Light border (used for separation lines)     |
|                    | `--border_dashed`                         | Dashed border for debugging/separators       |
| **Shadows**        | `--shadow_soft`                           | Soft drop shadow                             |
|                    | `--shadow_medium`                         | Medium drop shadow                           |
|                    | `--shadow_large`                          | Large drop shadow                            |
|                    | `--inset_shadow_large`                    | Large inset shadow for panels/inputs         |
|                    | `--inset_shadow_small`                    | Small inset shadow for inputs                |
|                    | `--text_shadow_hard`                      | Strong black outline around text             |
| **Gradients**      | `--gradient_fade_left`                    | Fade from left                               |
|                    | `--gradient_fade_right`                   | Fade from right                              |
|                    | `--gradient_fade_both`                    | Symmetrical fade in from both sides          |
| **Buttons**        | `--button_padding`                        | Padding inside buttons                       |
| **Scrollbars**     | `--scrollbar_track`                       | Background of scrollbars                     |
|                    | `--scrollbar_thumb`                       | Scrollbar draggable section                  |
| **Icons**          | `--icon_primary`                          | Color for primary icons                      |
|                    | `--icon_secondary`                        | Color for secondary icons                    |
| **Tooltips**       | `--tooltip_title`                         | Background/text color for tooltip title      |
|                    | `--tooltip_rarity_badge`                  | Background color for rarity badge in tooltip |
| **Namecard Ranks** | `--rank_bronze` to `--rank_legend`        | Rank color coding on namecards               |
| **Item Rarity**    | `--rarity_common` to `--rarity_legendary` | Color overlays for item rarity               |
| **Notifications**  | `--notify_success`                        | Color for success notifications              |
|                    | `--notify_error`                          | Color for error notifications                |
|                    | `--notify_info`                           | Color for info notifications                 |
|                    | `--notify_warning`                        | Color for warning notifications              |
|                    | `--notify_primary`                        | Used for general or system notices           |

***

## Step 3: Register Your Theme

Open `manifest.json` and make sure your theme is properly registered.

#### 1. Add your theme to the list:

```json
"themes": ["dark", "light", "mytheme"]
```

#### 2. Add the CSS file:

```json
"css": [
  ...
  "/ui/css/themes/mytheme.css"
]
```

#### 3. Set it as the default if you want it active:

```json
"default_theme": "mytheme"
```

> **Note:** Theme switching is not supported live yet — only the **default** theme is applied on load.

***

## That's It!

Your theme will now be used automatically when the UI loads.\
You can build as many themes as you want and change the default in the manifest to test each.

***

## &#x20;Coming Soon

* Support for dynamic theme switching in the builder
* Theme preview and selection components
* Theme-specific overrides for cards, modals, namecards, and more


# Core UI

<figure><img src="/files/5BFchZm64VkEzcekr2KQ" alt=""><figcaption><p>All core UI elements</p></figcaption></figure>

**BDUK** is split into two sections `core/` and `components` the core UI covers the following.

* **Header** **—** With slots for branding, buttons, and maybe one day a picture of your nan's cat.
* **Footer** **—** Button hints, status text, and keybind rows for people who forget controls.
* **Sidebar** **—** Optional side menu with submenu support.
* **Content** **—** Where your actual UI goes.. the meat and veg so to speak.
* **Tooltip** **—** Hover text system. Originally from my inventory UI. Now universal.

***

{% hint style="info" %}
Header sections can support any of the support components they are entirely interchangeable, you are not fixed to the layout shown in the example image.
{% endhint %}

## 🟨 1. Header (Left)

Used here for branding — includes:

* A `group` with a `logo` and `text`

***

## 🟥 2. Header (Center)

Used here for **tabs** navigation:

* Dynamically switch between pages using a `tabs` element.

***

## 🟩 3. Header (Right)

This section contains a **namecard** and optional **buttons** like `Save` and `Exit`.

***

## 🟦 4. Content (Left)

The core content section is broken down into 3 sections `left|center|right`, like the header and footer. \
You have full control over the layout of this section.

Currently covers two pre-built components:&#x20;

* Cards
  * Displays item cards in a grid layout.
  * Each card supports images, labels, embedded actions tooltips.. etc.
  * Can be used to make anything from crafting script UI's to vehicle stores.
* Input Groups
  * Displays groups of inputs in optional expandable containers.
  * Can be used to make clothing menus, vehicle custom options or anything else you can think of.

***

## 🟥 5. Tooltip

Tooltips display **on-hover** for cards or other items:

* Includes structured info: description, value pairs, actions.
* Can display arrays of lines and metadata like rarity.
* Supports key press actions on hover — Similar to how some survival games work.

This system is global and reusable — not limited to cards.

***

## 🟪 6. Sidebar

Optional and modular vertical menu:

* Contains sections with label + item groups.
* Items can have submenus and trigger actions.
* Perfect for admin menus or editor tools.

***

{% hint style="info" %}
Footer sections can support any of the support components they are entirely interchangeable, you are not fixed to the layout shown in the example image.
{% endhint %}

## 🟨 7. Footer (Left)

Standard **button group** placed on the left:

* `Deploy`, `Cancel`, etc.
* Can be any number of buttons and can include `modal` or `on_click` actions.

***

## 🟥 8. Footer (Centre)

Here it's a **text** element ("Ready to deploy"), but you can swap it with:

* Progress bar
* Page indicator
* Custom HTML/text component

***

## 🟩 9. Footer (Right)

A simple **keybind display** using `actions`:

* Shows user what key does what
* Like `ESC = Close`, `E = Confirm`

Supports dynamic binding for different workflows.

***

## Recap

Each section supports three alignment zones:

* `left`, `centre`, `right`
* Each zone can contain any number of supported components

| Area    | Description                         |
| ------- | ----------------------------------- |
| Header  | Branding, controls, nav, info       |
| Footer  | Actions, messaging, keybinds        |
| Content | The main view (multi-pane optional) |
| Sidebar | Expandable category menu            |
| Tooltip | Dynamic hover info popup            |


# Header

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

The `header` is the topmost part of the BDUK layout system. It is fully modular, with **three alignable sections** (`left`, `center`, and `right`) — you can place any supported component into any section.

***

## Structure

You define a `header` section using the following structure:

```lua
header = {
    layout = {
        left = { justify = "flex-start" },
        center = { justify = "center" },
        right = { justify = "flex-end" },
    },
    elements = {
        left = {
            {
                type = "group",
                items = {
                    { type = "logo", image = "bd_100.png" },
                    { type = "text", title = "BOII", subtitle = "UI Builder" }
                }
            }
        },
        center = {
            { type = "tabs" }
        },
        right = {
            {
                type = "namecard",
                avatar = "avatar_placeholder.jpg",
                background = "namecard_bg_4.jpg",
                name = "Player Name",
                title = "Some Player Title",
                level = 99,
                tier = "bronze"
            }
        }
    }
}
```

Each section (`left`, `center`, `right`) can be **included or omitted**, and you can insert any UI components inside — no limitations.

***

## Supported Elements

You can use any of the following types in **any** of the three header sections:

| `type`     | Description                                                  |
| ---------- | ------------------------------------------------------------ |
| `logo`     | Displays a logo image via background.                        |
| `text`     | Title + optional subtitle, stacked vertically.               |
| `tabs`     | Auto-generated tab navigation from `content.pages`.          |
| `button`   | Single-action button element.                                |
| `buttons`  | Button group using the `Buttons` class.                      |
| `group`    | A horizontal wrapper that nests multiple child elements.     |
| `namecard` | Visual identity block with avatar, name, title, tier, level. |

***

## Alignment Control

Each section's alignment can be styled independently using:

```lua
layout = {
    left = { justify = "flex-start", align = "center", gap = "1vw" },
    ...
}
```

These values map directly to Flexbox properties for layout precision.


# Content

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

The `content` section defines the **main UI area**, supporting **left**, **centre**, and **right** regions. \
Each region can display structured components like input groups or cards, and the layout is fully configurable.

It acts as the core of your UI layout system, with full support for modular pages and responsive content updates.

***

The above image shows the following:&#x20;

🟩 1. Cards in column layout.\
🟥 2. Input Groups\
🟦 3. Cards in Row layout

***

## Configuration

Each content page consists of:

* `index`: (optional) Determines order or default tab position.
* `title`: Displayed in tabs or page headers.
* `layout`: Defines column spans (`left`, `center`, `right`).
* `left`, `center`, `right`: Each region may contain a component block.

```lua
content = {
    pages = {
        my_page = {
            title = "My Page",
            index = 1,
            layout = { left = 2, center = 6, right = 4 },
            left = { type = "cards", title = "Left Section", cards = { ... } },
            center = { type = "input_groups", title = "Center Section", groups = { ... } },
            right = { type = "cards", title = "Right Section", cards = { ... } }
        }
    }
}
```

***

## Content Types

### Cards

Used for displaying visual blocks with optional images, descriptions, hover tooltips, and buttons.

**Example:**

```lua
{
    type = "cards",
    layout = { columns = 1, flex = "row", scroll_x = "scroll", scroll_y = "scroll" },
    title = "Right Section",
    cards = {
        {
            image = "https://placehold.co/64x64",
            title = "Card In Row",
            description = "Card Description.",
            layout = "row",
            on_hover = {
                title = "Card Info",
                description = { "Info descriptions can support arrays", "- like so", "- you get the idea" },
                values = {
                    { key = "Key", value = "Value Pairs" },
                    { key = "Name", value = "Case" }
                },
                actions = {
                    { id = "test_action", key = "E", label = "Action on Keypress", on_keypress = function(data) ... end  }
                },
                rarity = "common"
            },
            buttons = {
                {
                    id = "modal_btn",
                    label = "Modal Button",
                    class = "primary",
                    on_action = function(data) ... end,
                    dataset = {
                        target_id = "example_card",
                        source = "cards_test"
                    },
                    modal = {
                        title = "Modal Title",
                        options = {
                            { id = "name", label = "Name", type = "text" }
                        },
                        buttons = {
                            { id = "modal_button", label = "Modal Button 2", on_action = function(data) ... end }
                        }
                    }
                }
            }
        }
    }
}
```

***

### Input Groups

Used to organize structured form inputs into expandable groupings. \
Can include buttons for submitting or interacting with input data.

**Example:**

```lua
{
  type = "input_groups",
  title = "Input Groups",
  id = "inputs_id",
  layout = { columns = 1 },
  groups = {
    {
      header = "Some Options",
      expandable = false,
      inputs = {
        { id = "opt_1", type = "text", label = "Some Option" },
        { id = "opt_2", type = "number", label = "Another Option" }
      }
    }
  },
  buttons = {
    { id = "input_btn", label = "Input Button", class = "primary", on_action = function(data) ... end }
  }
}
```


# Footer

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

The `footer` sits at the bottom of the BDUK UI layout and serves as a **flexible, function-rich area** for actions, buttons, hotkeys, tooltips, and even profile info. It follows the same `left`, `center`, and `right` layout system as the `header`.

***

## Structure

You define a `footer` like so:

```lua
footer = {
    layout = {
        left = { justify = "flex-start", gap = "1vw" },
        center = { justify = "center" },
        right = { justify = "flex-end", gap = "1vw" },
    },
    elements = {
        left = {
            {
                type = "buttons",
                buttons = {
                    { id = "deploy", label = "Deploy", on_action = function(data) ... end, class = "primary" },
                    { id = "cancel", label = "Cancel", action = "close_builder", class = "secondary" }
                }
            }
        },
        center = {
            { type = "text", text = "Ready to deploy." }
        },
        right = {
            {
                type = "actions",
                actions = {
                    { key = "ESC", label = "Close" },
                    { key = "E", label = "Confirm" }
                }
            }
        }
    }
}
```

You may omit or include any section (`left`, `center`, `right`) and place elements in whichever position you need. All elements are fully swappable.

***

## Supported Elements

The `footer` supports a range of useful UI components:

| `type`     | Description                                             |
| ---------- | ------------------------------------------------------- |
| `text`     | Simple centered or aligned string label.                |
| `action`   | Single hotkey + label (e.g. `ESC = Close`).             |
| `actions`  | Group of hotkeys displayed inline.                      |
| `buttons`  | One or more clickable buttons with callbacks.           |
| `group`    | Inline wrapper for grouping elements together.          |
| `namecard` | Same identity block used in the header (optional here). |

Each type is handled internally by the `Footer` class with intelligent layout styling and optional click hooks.

***

## Alignment Control

You can define layout styles for each section using `justify`, `align`, and `gap` just like in the header:

```lua
left = {
    justify = "flex-start",
    align = "center",
    gap = "1vw"
}
```

These map directly to CSS flexbox properties.


# Sidebar

<div align="center"><figure><img src="/files/SUGg9RCdrvNURbbfT8v3" alt="" width="196"><figcaption></figcaption></figure></div>

The `sidebar` is a **vertical action menu** placed to the left or right of your UI layout. \
It supports nested submenus, icons, images, and dataset-driven interactivity.

***

## Structure

```lua
sidebar = {
    layout = { side = "right" }, -- or "left"
    sections = {
        {
            id = "test_1",
            label = "Sidebar Category",
            items = {
                {
                    id = "some_option",
                    label = "Some Option",
                    image = "assets/logos/bd_100.png",
                    action = "some_action"
                },
                {
                    id = "some_other_option",
                    label = "Some Other Option",
                    icon = "fas fa-box",
                    action = "some_other_action",
                    submenu = {
                        { id = "some_submenu_option_1", label = "Some Submenu Option", on_action = function(data) ... end },
                        { id = "some_submenu_option_2", label = "Some Other Submenu Option", on_action = function(data) ... end }
                    }
                }
            }
        }
    }
}
```

You can define multiple `sections` and group your interactions logically by category.

***

## Item Options

Each `section` contains a list of `items`. These support:

| Field     | Description                                         |
| --------- | --------------------------------------------------- |
| `id`      | Unique identifier used for click callbacks.         |
| `label`   | Text label shown in the sidebar.                    |
| `action`  | Action string sent to the client (Lua) for routing. |
| `icon`    | Font Awesome icon class.                            |
| `image`   | Local or web image path.                            |
| `submenu` | Optional array of sub-options, shown on click.      |

Submenu items are structured identically but don’t support icons or images.

***

## Layout

You can place the sidebar on either side:

```lua
layout = { side = "left" } -- or "right"
```

Internally, this maps to a `.sidebar.left` or `.sidebar.right` class with automatic CSS control.


# Tooltip

<figure><img src="/files/5uhJqtDkEf7CUKcDUNx9" alt=""><figcaption></figcaption></figure>

The `tooltip` is a dynamic info panel that follows the mouse and displays contextual data for any UI element that has hover metadata.

It supports:

* **Title, description, and key-value fields**
* **Rarity styling**
* **Future keypress-based action hints**
* Full integration using `on_hover` blocks

***

## Structure

Tooltips are defined on any UI element via the `on_hover` table:

```lua
{
    title = "Card Info",
    description = {
        "Info descriptions can support arrays",
        "- like so",
        "- you get the idea"
    },
    values = {
        { key = "Key", value = "Value Pairs" },
        { key = "Name", value = "Case" }
    },
    actions = {
        { 
            id = "test_action", 
            key = "E", 
            label = "Action on Keypress", 
            on_action = function(data) ... end
        }
    },
    rarity = "common"
}
```

Attach this block using `on_hover` key in UI config.

***

## Rarity Themes

Tooltip headers adapt colours based on `rarity`. \
This uses `var(--rarity_x)` theming for visual identity.

| Value       | Style CSS Variable   |
| ----------- | -------------------- |
| `uncommon`  | `--rarity_uncommon`  |
| `common`    | `--rarity_common`    |
| `rare`      | `--rarity_rare`      |
| `epic`      | `--rarity_epic`      |
| `legendary` | `--rarity_legendary` |

```css
--rarity_common: rgba(255, 255, 255, 0.8);
--rarity_uncommon: rgba(38, 189, 38, 0.8);
--rarity_rare: rgba(66, 135, 245,  0.8);
--rarity_epic: rgba(119, 45, 189,  0.8);
--rarity_legendary: rgba(219, 144, 57,  0.8);
```

***

## Tooltip Sections

Each tooltip supports 4 sections:

| Section       | Description                                        |
| ------------- | -------------------------------------------------- |
| `title`       | The main tooltip title with optional rarity label. |
| `description` | Array of text lines shown as a paragraph block.    |
| `values`      | Key-value display for stats, attributes, etc.      |
| `actions`     | Shows a list of interactive keypresses.            |


# Components

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

**BDUK** includes a growing list of UI components that you can mix, match, and reuse across headers, footers, sidebars, and main content areas.

This example layout showcases multiple core components working together in one interface. \
Each element is modular and can be placed anywhere **that supports that type of component**.

{% hint style="info" %}
The layout shown is just an example — you are not locked into any positioning.
{% endhint %}

***

## 🟩 **1. Cards (Column)**

Displayed in the **left section** of the content layout.\
Cards are visual containers that support:

* Images, labels, descriptions
* Embedded buttons or modals
* Hover tooltips for extra info
* Flexible layouts: column or row

**Usage:** Content only

***

## 🟥 **2. Input Groups**

Displayed in the **centre section**.\
Grouped input elements under titled headers:

* Supports expanding/collapsing groups
* Includes text, number, select and more
* Pre-styled and layout-ready

**Usage:** Content only

***

## 🟦 **3. Cards (Row)**

Displayed in the **right section**, using a scrollable row layout.\
Behaves like column cards but flows horizontally:

* Set `layout.flex = "row"`
* Combine with scroll for carousels or horizontal menus

**Usage:** Content only

***

## 🟨 **4. Namecard**

Displayed in the **top right** of the header, but can also go in the footer.\
Compact user block showing:

* Player name/title
* Optional avatar, tier, or badge

**Usage:** Header or Footer

***

## 🟪 **5. Notifications**

Shown as floating stack elements — typically top right.\
Display system-wide messages or alerts:

* Color-coded by type or rarity
* Icon, border, and shadow support
* Auto-dismiss or persist as needed

**Usage:** Global (top-level only)

***

## 🟫 **6. Modal**

Popup displayed in centre of screen on action.\
Used for inline editing, confirmation, or input:

* Title, inputs, and action buttons
* Optional dataset for advanced logic

**Usage:** Triggered from any component button or keypress.

***

## 🟥 **7. Buttons**

Buttons are one of the most flexible and essential components in BDUK.\
They can be used anywhere that accepts components, including:

* Headers
* Footers
* Content sections
* Sidebars
* Modals

Each button supports:

* Dataset attributes passed to Lua or other NUI handlers
* Optional modals defined inline (`data-modal`)
* Optional `should_close` flag to auto-close the UI builder after action

Buttons are automatically bound to handle modal opening and NUI callbacks without any extra setup. You can define as many as you want in a group.

&#x20;**Usage:** Any layout section or modal

***

## Recap

Every component in BDUK is modular, but not every component belongs everywhere:

{% hint style="info" %}
Notifications and Modals are top level elements, they are not placed inside any specific section.
{% endhint %}

| Component    | Header | Footer | Content | Sidebar |
| ------------ | ------ | ------ | ------- | ------- |
| Cards        | ❌      | ❌      | ✅       | ❌       |
| Input Groups | ❌      | ❌      | ✅       | ❌       |
| Namecard     | ✅      | ✅      | ❌       | ❌       |
| Buttons      | ✅      | ✅      | ✅       | ✅       |


# Buttons

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

Adds one or more clickable buttons to your layout.\
Use these for confirming actions, opening modals, or triggering logic through custom `on_action` handlers.

***

## Structure

```lua
buttons = {
    {
        id = "confirm_btn",
        label = "Confirm",
        on_action = function(data)
            -- Custom logic for confirming a purchase
        end,
        should_close = true,
        class = "primary"
    },
    {
        id = "cancel_btn",
        label = "Cancel",
        action = "close_builder",
        class = "danger"
    }
}
```

***

## Options

| Key            | Description                                                                 |
| -------------- | --------------------------------------------------------------------------- |
| `id`           | Unique identifier for the button.                                           |
| `label`        | Text shown on the button.                                                   |
| `action`       | (Optional) Use `"close_builder"` to close the UI safely.                    |
| `on_action`    | (Optional) A function to run when clicked. Receives `data` if needed.       |
| `should_close` | (Optional) Closes the UI builder after clicking. Default: `false`.          |
| `class`        | (Optional) Style of button — try `"primary"`, `"danger"`, `"success"`, etc. |
| `icon`         | (Optional) Add an icon, e.g., `"fas fa-check"`.                             |
| `modal`        | (Optional) Show a confirmation modal before running the action.             |
| `dataset`      | (Optional) Extra data to pass to the `on_action` function.                  |

***

## Notes

* Use multiple buttons in a row by adding more entries to the `buttons` table.
* `dataset = {}` gets passed into your `on_action` function.
* Use `should_close = true` if the UI should close after pressing the button.
* Use `modal = { title = "...", description = "..." }` to prompt confirmation before the button runs its logic.
* For closing the UI, use the special global action:

  ```lua
  action = "close_builder"
  ```


# Cards

<div><figure><img src="/files/I8uvsVE02VY4nxUBAGcI" alt=""><figcaption></figcaption></figure> <figure><img src="/files/2MIQsv2A6AFzCdJIAg7R" alt=""><figcaption></figcaption></figure></div>

Displays one or more card-style panels in a grid or list format.\
Cards can contain an image, a title, a description, tooltips on hover, and interactive buttons or keybind actions.

***

## Structure

```lua
left = {
    type = "cards",
    title = "Items",
    layout = {
        columns = 2,
        flex = "column",
        scroll_x = "none"
    },
    cards = {
        {
            image = "https://placehold.co/252x126",
            title = "My Card",
            description = "This is a test card.",
            layout = "column",
            on_hover = {
                title = "Extra Info",
                description = { "You can use", "- multiple lines", "- like this" },
                values = {
                    { key = "Item", value = "Something" },
                    { key = "Owner", value = "Case" }
                },
                actions = {
                    {
                        id = "inspect_item",
                        key = "E",
                        label = "Inspect",
                        on_action = function(data)
                            -- Inspect logic
                        end
                    }
                },
                rarity = "common"
            },
            buttons = {
                {
                    id = "edit_card",
                    label = "Edit",
                    class = "primary",
                    on_action = function(data)
                        -- Edit card logic
                    end,
                    dataset = {
                        card_id = "card_1"
                    }
                },
                {
                    id = "close_ui",
                    label = "Close",
                    action = "close_builder",
                    class = "danger"
                }
            }
        }
    }
}
```

***

## Card Options

Each card supports the following fields:

| Key             | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| `title`         | Title text for the card.                                                  |
| `description`   | Text shown below the title.                                               |
| `image`         | (Optional) Image shown at the top. Full URLs or `nui://` paths supported. |
| `layout`        | (Optional) `"column"` or `"row"` layout inside the card.                  |
| `on_hover`      | (Optional) Shows tooltip with extra info:                                 |
| → `title`       | Tooltip header.                                                           |
| → `description` | List of lines for the tooltip.                                            |
| → `values`      | Array of `{ key, value }` to show below tooltip.                          |
| → `actions`     | Keybinding actions with `on_action` support.                              |
| → `rarity`      | (Optional) A style tag such as `"common"`, `"rare"`, etc.                 |
| `buttons`       | (Optional) Array of button objects (same structure as Buttons component). |

***

## Layout Options

| Key        | Description                                                |
| ---------- | ---------------------------------------------------------- |
| `columns`  | Number of columns in the card layout (e.g., `2`).          |
| `flex`     | `"row"` or `"column"` for internal card layout.            |
| `scroll_x` | `"auto"`, `"on"`, or `"none"` to enable horizontal scroll. |
| `scroll_y` | `"auto"`, `"on"`, or `"none"` to enable vertical scroll.   |

***

## Notes

* Cards are perfect for displaying item lists, inventories, player entries, or shops.
* You can freely mix images, hover tooltips, and buttons per card.
* Each `button` or `action` supports:
  * `on_action = function(data)` — your custom logic.
  * `action = "close_builder"` — a built-in safe UI close.
  * Optional `dataset = {}` to pass values to the function.
* This structure keeps your logic uniform across both `cards.buttons` and `cards.on_hover.actions`.


# Input Groups

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

Organize grouped input fields in structured containers.\
Supports expandable sections, number inputs with increment/decrement handlers, and custom action buttons.

***

## Structure

```lua
center = {
    type = "input_groups",
    id = "test_inputs",
    title = "Input Groups Test",
    layout = {
        columns = 1,
        scroll_x = "none",
        scroll_y = "auto"
    },
    groups = {
        {
            header = "Some Group",
            expandable = false,
            inputs = {
                {
                    id = "option_1",
                    type = "number",
                    label = "Some Option",
                    category = "group_1",
                    on_increment = function(data)
                        print("Incremented")
                    end,
                    on_decrement = function(data)
                        print("Decremented")
                    end
                }
            }
        }
    },
    buttons = {
        {
            id = "confirm_options",
            label = "Confirm",
            action = "confirm_options",
            class = "primary",
            dataset = {
                target_id = "test_inputs",
                source = "input_groups_test"
            }
        },
        {
            id = "reset_options",
            label = "Reset",
            action = "reset_options",
            class = "secondary",
            dataset = {
                target_id = "test_inputs",
                source = "input_groups_test"
            }
        }
    }
}
```

***

## Group Options

| Key          | Description                          |
| ------------ | ------------------------------------ |
| `header`     | Title displayed above the group.     |
| `expandable` | Allows the group to collapse/expand. |
| `inputs`     | Array of fields in the group.        |

***

## Input Options

| Key            | Description                                                                 |
| -------------- | --------------------------------------------------------------------------- |
| `id`           | Unique input identifier.                                                    |
| `type`         | `"number"` or `"text"`.                                                     |
| `label`        | Label shown next to the input.                                              |
| `category`     | (Optional) Logical grouping for identification.                             |
| `default`      | (Text only) Initial value shown in the input.                               |
| `placeholder`  | (Text only) Shown when the field is empty.                                  |
| `on_increment` | (Number only) Function triggered when + button is pressed. Receives `data`. |
| `on_decrement` | (Number only) Function triggered when – button is pressed. Receives `data`. |

> Number inputs automatically include + and – buttons for value adjustment. These fire your attached callbacks with context-aware `data` from `dataset`.

***

## Button Options

| Key            | Description                                                           |
| -------------- | --------------------------------------------------------------------- |
| `id`           | Unique button name.                                                   |
| `label`        | Text shown on the button.                                             |
| `action`       | Action to trigger — e.g., `"close_builder"` or your own NUI callback. |
| `on_action`    | (Optional) Function to run instead of action.                         |
| `class`        | (Optional) Button style: `"primary"`, `"danger"`, etc.                |
| `should_close` | (Optional) Whether to close the UI on press. Default: `false`.        |
| `dataset`      | (Optional) Custom data passed to the action or function.              |

***

## Layout Options

| Key        | Description                                          |
| ---------- | ---------------------------------------------------- |
| `columns`  | Number of input columns (e.g., 2 = side by side).    |
| `scroll_x` | `"none"`, `"auto"`, or `"on"` for horizontal scroll. |
| `scroll_y` | `"none"`, `"auto"`, or `"on"` for vertical scroll.   |

***

## Notes

* `on_increment` and `on_decrement` are the preferred way to handle number field changes.
* Group sections can be collapsed by setting `expandable = true`.
* Use `dataset` to pass `target_id`, `source`, or custom flags with buttons.
* Button logic and dataset handling are identical to the Buttons component for consistency.
* Use `action = "close_builder"` if you want to safely exit the UI.


# Modal

<figure><img src="/files/5kT2EI9bQF1u2zuSS35F" alt=""><figcaption></figcaption></figure>

A popup window that shows a title, input fields, and action buttons.\
Modals can appear when you press a key, hover over a card, or click a button.

***

## Structure

```lua
modal = {
    title = "Edit Item",
    options = {
        { id = "item_name", label = "Item Name", type = "text" },
        { id = "amount", label = "Amount", type = "number", min = 1, max = 100 }
    },
    buttons = {
        {
            id = "save_item",
            label = "Save",
            on_action = function(data)
                -- do something with data.dataset.item_name, data.dataset.amount
            end,
            dataset = { -- additional data if required
                source = "inventory",
                item_id = "some_id"
            }
        },
        {
            id = "cancel",
            label = "Cancel"
            -- No action needed — this will auto-close the modal
        }
    }
}
```

***

## Modal Options

| Key       | Description                                                  |
| --------- | ------------------------------------------------------------ |
| `title`   | Title text shown at the top of the modal.                    |
| `options` | A list of inputs to show inside the modal (see Input Types). |
| `buttons` | Buttons shown at the bottom — use `on_action` or `action`.   |

***

## Input Types

| Type       | Description                                                      |
| ---------- | ---------------------------------------------------------------- |
| `text`     | A single-line text input.                                        |
| `number`   | A number input. You can include `min` and `max`.                 |
| `textarea` | A larger, multiline input.                                       |
| `select`   | A dropdown menu — include `options = { { label, value }, ... }`. |

## Each input requires:

| Key     | Description                        |
| ------- | ---------------------------------- |
| `id`    | Unique field name.                 |
| `label` | Label displayed next to the input. |

***

## Button Options

| Key         | Description                                                           |
| ----------- | --------------------------------------------------------------------- |
| `id`        | Unique button ID.                                                     |
| `label`     | Button text.                                                          |
| `on_action` | Custom handler function that receives `{ input = {}, dataset = {} }`. |
| `action`    | Optional named action (e.g., `"close_builder"`).                      |
| `dataset`   | Optional values passed alongside the input data.                      |
| *(none)*    | If no `on_action` or `action` is given, the button closes the modal.  |

***

## Notes

* Modal input values are **automatically collected** when a button is clicked.
* Values are passed to your handler function or action as `data.input`.
* You can show one modal per interaction or button — they do not conflict.
* Use `on_action = function(data)` for fully custom behavior.
* Use `action = "close_builder"` to safely close the UI entirely.
* Simply leave out the action on any button to close the modal.


# Namecard

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

Displays a clean, styled player profile — including avatar, name, title, level, and tier.\
Used in headers or footers to show who's logged in, their status, or game rank.

***

## Structure

```lua
{
    type = "namecard",
    avatar = "avatar_1.jpg",
    background = "namecard_bg_2.jpg",
    name = "Player Name",
    title = "Some Player Title",
    level = 99,
    tier = "silver"
}
```

***

## Where to Use

You can place a namecard in:

* `header.left`, or `header.right`
* `footer.left`, or `footer.right`

***

## Fields

* **`type`**: Always `"namecard"` to use this component.
* **`avatar`**: File name or path for the avatar image (stored in `/ui/assets/namecards/avatars/`). If a full URL or `nui://` path is provided, it will be used directly.
* **`background`** *(optional)*: Background image file (stored in `/ui/assets/namecards/backgrounds/`). Defaults to `"avatar_placeholder.png"`. Full links or `nui://` paths are supported.
* **`name`** *(optional)*: The player’s name. Defaults to `"Player Name"`.
* **`title`** *(optional)*: Role or description (e.g., "Founder", "Police Chief").
* **`level`** *(optional)*: Player level — displayed as `Lv. 99`.
* **`tier`** *(optional)*: Style pre-set (`"bronze"`, `"silver"`, `"gold"`, etc.) for colouring and badges.

***

## Notes

* `avatar` and `background` must match filenames in the asset folders.
* Tiers can be styled via CSS — each one has a visual style (`.bronze`, `.gold`, etc.).


# Notify

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

Shows floating notifications on the screen to alert the player.\
Use these for messages like success, errors, info, warnings, etc.\
\
These were added mainly to provide a built in way for notifying from within the UI, however you can use the notifications externally if you wish.

***

## Usage

You **trigger notifications** using component `notify` sections, directly though `exports.bduk:notify(opts)`, or by firing the client event `bduk:notify`

***

## Structure

```lua
{
    type = "success",
    message = "Item added to inventory!",
    header = "Success",
    icon = "fa-solid fa-check-circle",
    duration = 5000,
    match_border = true,
    match_shadow = true
}
```

***

## Notification Options

* **`type`**: `"info"`, `"success"`, `"warning"`, or `"error"` — changes the color.
* **`header`** *(optional)*: Bold title at the top.
* **`message`**: Main text body.
* **`icon`** *(optional)*: Font Awesome icon class (e.g. `"fa-solid fa-check"`).
* **`duration`** *(optional)*: How long it stays on screen (in ms). `0` = sticky.
* **`match_border`** *(optional)*: Color the border to match the type.
* **`match_shadow`** *(optional)*: Color the shadow to match the type.

***

## Position & Direction

You can configure where and how notifications appear using within the main builder config:

<pre class="language-lua"><code class="lang-lua"><strong>notify = {
</strong>    position = "top-right",      -- top-left, top-center, bottom-right, etc.
    fill_direction = "down"      -- "down" (default) or "up"
}
</code></pre>

If you don't specify this, it defaults to `"right-center"` filling **downward**.

***

#### Notes

* Notifications are **always floating** — they don’t go in layout slots.
* Supports multiple styles and customizable timing.
* Can stack multiple at once.


# API

This page documents the public exports and events provided by BDUK.

***

## Events

### bduk:notify

Fires a UI notification  to the client.

```lua
TriggerEvent("bduk:notify", {
    type = "success",
    message = "Item added!",
    header = "Done",
    icon = "fa-solid fa-check-circle",
    duration = 5000,
    match_border = true,
    match_shadow = false
})
```

#### Parameters

* `type`: `"success"`, `"error"`, `"info"`, `"warning"` — *(default: `"info"`)*
* `message`: Body text (required)
* `header`: Optional title
* `icon`: Font Awesome class (e.g. `"fa-solid fa-bell"`)
* `duration`: Time in ms (default `5000`, `0` = sticky)
* `match_border`: Whether to color the border based on type
* `match_shadow`: Whether to color the shadow based on type

***

## Exports

### build

Builds and displays a full BDUK UI layout.

```lua
exports.bduk:build(layout)
```

#### Parameters

* `layout`: The full Lua config table for your UI (see: [**Making Your First UI**](/fivem-free-resources/bduk/guides/making-your-first-ui))

***

### notify

Triggers a notification programmatically (same as the `bduk:notify` event).

```lua
exports.bduk:notify({
    type = "error",
    message = "Something went wrong!"
})
```

#### Parameters

* Same structure as `bduk:notify` event (see above)


# BDSC

A **lightweight**, **modern** server core designed for maximum flexibility.

{% hint style="danger" %}

### Not a Roleplay Framework

BDSC is **not** a traditional server framework.\
It ships with **no jobs, inventories, or gameplay logic**.

What your server does is **entirely defined** by the plugins you choose to load — or build yourself.
{% endhint %}

***

## What Is BDSC?

Normally? **BOII Development Server Core**\
When it’s running smoothly? **Barebones Done Smart & Clean**\
When it refuses to co-operate? **Bug-Driven Sh\*tfest of Chaos.**

BDSC is a lightweight server foundation designed for developers who want control.\
It doesn’t care what your server does — it just gives you a clean, flexible structure to build on.

No jobs.\
No inventory.\
No economy.\
No drama.

You get the boring but essential stuff:

* Player management
* Object extensions
* A couple of handy utilities\
  Everything else? You decide.

***

## Who It’s For

BDSC was built for internal use on BOII projects — survival, minigames, experiments.\
But it’s now open and modular enough to support anything.

If you're tired of bloated RP frameworks or want a **clean, no-bullshit starting point**, this is for you.

You get:

* A stable foundation
* A consistent object system
* A fully extensible runtime
* Zero assumptions about your gameplay

Use it. Fork it. Rewrite it. Just don’t make it worse.

***

## What It Provides

BDSC handles:

* Player management and object lifecycle
* Extension hooks for runtime systems
* Server/client-safe data syncing
* Core exports for structured access
* Some basic utlity functions

No hardcoded gameplay.\
No required systems.\
No enforced dependencies.\
You build what matters — BDSC stays out of the way.

***

## Ideal Use Cases

BDSC is framework-agnostic and gameplay-neutral.\
Perfect for:

* Custom survival servers
* Minigame or PvP modes
* Custom RP frameworks
* Experimental mechanics
* Hybrid / mashup servers

If you’re building something new, this is the foundation to do it clean.

***

## Player Extensions

BDSC supports extending player objects at runtime - without breaking structure:

```lua
player:add_data("stats", { health = 100 }, true)
player:add_method("stats", "get_health", function(self) return self:get_data("stats").health end)
player:run_method("stats", "get_health")
```

All data/methods are namespaced, and replicated data syncs automatically to clients if marked as such.

***

## Structure

Everything is modular and clearly separated.

```
bashCopyEditcore/
│
├── lib/              # Utility functions (e.g. utils.lua)
│
├── player/           # Main player system
│   ├── events.lua       # Join/leave, sync, client handlers
│   ├── factory.lua      # Creates player objects
│   ├── methods.lua      # Public/private extension logic
│   ├── registry.lua     # Player storage, save/remove/get
│
├── locales/          # Translation files
│   └── en.lua
│
├── finalise.lua      # Exports and locks bdsc namespace
├── fxmanifest.lua    # Resource manifest
└── init.lua          # Core bootloader
```

Want to add inventory? Authentication? Stats?\
Just attach logic using `add_data` and `add_method` on player objects - no plugin boilerplate required.

***

## Quick Install

BDSC isn’t a full framework — it’s a clean server **core**. \
You don’t need to install databases, jobs, inventories, or other bloated systems.

To get started:

{% stepper %}
{% step %}

#### **Drop It Into Your Server**

Place the `bdsc` resource into your `resources/` folder.\
Make sure it starts after `bdtk` (required for user accounts, you could change this).

```
ensure bdtk
ensure bdsc
```

{% endstep %}

{% step %}

#### **Start The Server**

BDSC will automatically initialize and log connected players.\
It doesn’t require SQL or any config to boot.
{% endstep %}

{% step %}

#### Extend It

Write your own logic using the exposed player API, or import it into your systems via:

```lua
local core = exports.bdsc:import()
```

That’s it. No setup wizard. No bloated dependencies.\
Just a clean foundation for your own logic.
{% endstep %}
{% endstepper %}

***

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Important Guides</td><td><a href="/files/mXGQlzqtxWDAWC63HwSh">/files/mXGQlzqtxWDAWC63HwSh</a></td><td><a href="/pages/ReibOVNagqQM9oAUvXBH">/pages/ReibOVNagqQM9oAUvXBH</a></td></tr><tr><td>API Reference</td><td><a href="/files/lfc1oyGmqtwJalCePlXA">/files/lfc1oyGmqtwJalCePlXA</a></td><td><a href="/pages/tG7Hg9wkmpdNXg3iRGpY">/pages/tG7Hg9wkmpdNXg3iRGpY</a></td></tr></tbody></table>


# Guides

Welcome to the BDSC Guides section.\
This area covers everything you need to know to **extend**, **modify**, and **interact** with player objects using the BDSC server core.

Whether you're adding custom stats, building a role system, or just syncing data. \
These guides walk you through the process with real examples.

> All guides assume you’ve already set up the core system and are familiar with Lua in a server-side FiveM environment.

***

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td>Extending Players</td><td><a href="/pages/TXP11MeT8ciDKyMYjPd9">/pages/TXP11MeT8ciDKyMYjPd9</a></td><td><a href="/files/KQrHHTGU9BKAX4iXpBCC">/files/KQrHHTGU9BKAX4iXpBCC</a></td></tr><tr><td>Working Examples</td><td><a href="/pages/aX2lseLrF4SEupqEaJAd">/pages/aX2lseLrF4SEupqEaJAd</a></td><td><a href="/files/JJZ7xyl93OBMA1aPd1fs">/files/JJZ7xyl93OBMA1aPd1fs</a></td></tr></tbody></table>


# Extending Players

BDSC supports dynamic, runtime-safe extension of player objects with custom data and methods. \
This lets you build modular logic without touching the core.

***

## Add Data

Adds custom data to the player. Optionally replicated to the client.

**Example**

```lua
player:add_data("stats", { health = 100, stamina = 50 }, true)
```

**Parameters**

* `stats` – key name for the data entry.
* `{ health = 100, stamina = 50 }` – table to store.
* `true` – replicate to client (optional).

***

## Add Methods

Attach custom methods that can later be executed via `run_method`.

**Example**

```lua
player:add_method("stats", "get_health", function(self)
    local stats = self:get_data("stats")
    return stats and stats.health or 0
end)
```

**Usage**

```lua
player:run_method("stats", "get_health")
```

***

## Update Data

Modify an existing data block and optionally sync it.

**Example**

```lua
local stats = player:get_data("stats")
stats.health = stats.health - 10
player:set_data("stats", stats, true)
```

***

## Remove Data

Removes a data key.

**Example**

```lua
player:remove_data("job")
```

***

## Sync Data

Force a sync of all replicated data to the client.

**Example**

```lua
player:sync_data()
```

***

## Dump All Data

Logs all currently stored player data.

**Example**

```lua
local data = player:get_data()
for k, v in pairs(data) do
    print(k .. ": " .. json.encode(v))
end
```

***

Here’s a concise section explaining how the `on_save` and `on_destroy` methods work when registered via namespaces:

***

## Lifecycle Hooks

BDSC supports **namespace-specific lifecycle methods** that automatically run when certain player events occur.

These methods are defined under a specific namespace using `add_method` and are called internally during key moments in the player's lifecycle.

#### Supported Hooks

| Hook Name    | Description                                                                 |
| ------------ | --------------------------------------------------------------------------- |
| `on_save`    | Called when `player:save()` is triggered (e.g., on disconnect or manually). |
| `on_destroy` | Called when `player:destroy()` is triggered (e.g., on player drop).         |

#### How It Works

When a lifecycle event runs, BDSC will automatically check each namespace for these methods and call them in order.

#### Example

```lua
player:add_method("stats", "on_save", function(self)
    print("Saving stats for player:", self.meta.username)
end)

player:add_method("inventory", "on_destroy", function(self)
    print("Cleaning up inventory for:", self.meta.username)
end)
```

You **do not** need to manually call these - BDSC will handle them as long as they are added under a namespace using `add_method(namespace, "on_save" | "on_destroy", fn)`.

> Avoid long-running operations inside these hooks — keep them lightweight for stability.

***

## Missing Method Safety

Calling a non-existent method will return `nil`.

**Example**

```lua
local result = player:run_method("missing")
print("Result:", result or "nil")
```

***

## No Internal Access

Only the `meta` table is exposed. All other functionality data, methods, syncing - must be accessed via the public API.

Direct access to `_extensions._data`, `_extensions._methods`, or other internals is not possible and never required.

***

## Quick Example

Below is a minimal, self-contained example that demonstrates how to create a player object and extend it externally using basic commands.

This includes:

* Adding a `stats` data block
* Defining custom methods (like `get_health`)
* Syncing data to the client
* Simulating damage
* Handling save logic

```lua
local core = exports.bdsc:import()

-- Create and initialize a new player
RegisterCommand("make", function(src)
    local player = core.create_player(src)
    if not player then print("Failed to create player") return end

    -- Add initial stats
    player:add_data("stats", {
        health = 100,
        stamina = 50
    }, true)

    -- Add method to retrieve health
    player:add_method("stats", "get_health", function(self)
        local stats = self:get_data("stats")
        return stats and stats.health or 0
    end)

    -- Add save lifecycle method
    player:add_method("stats", "on_save", function()
        print("saving player stats")
    end)

    print("Player created and methods attached.")
end, false)

-- Check player health
RegisterCommand("hp", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local hp = player:run_method("stats", "get_health")
    print("Health:", hp)
end, false)

-- Apply damage to the player
RegisterCommand("damage", function(src, args)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local amount = tonumber(args[1]) or 10
    local stats = player:get_data("stats")
    stats.health = stats.health - amount

    player:set_data("stats", stats, true)
    print("Damaged player for", amount)
end, false)
```

> **Tip:** Every player object supports runtime extension. You can safely add new data or methods at any point — even after creation.


# Full Working Example

This example demonstrates how to use BDSC to create and interact with custom player data and methods.

It includes:

* Creating a player instance
* Attaching data (`add_data`, `set_data`, `remove_data`)
* Adding custom methods (`add_method`)
* Running methods (`run_method`)
* Lifecycle support (`on_save`, `on_destroy`)
* Syncing data to the client
* Safe protections against modifying internal structures

> T**ip:** This file can be placed directly into a test resource and will work as-is with a functioning BDSC + BDTK environment.

***

#### 📄 `example.lua`

```lua
--[[ 
    This file is part of BDSC (BOII Development Server Core) and is licensed under the MIT License.
    See the LICENSE file in the root directory for full terms.

    © 2025 Case @ BOII Development

    Support honest development — retain this credit. Don't be that guy...
]]

--- @script example
--- @description BDSC Example Integration File
--- This file demonstrates how to:
--- - Create a player instance
--- - Add custom data and methods
--- - Access and modify data
--- - Trigger lifecycle and sync logic

--- Importing the bdsc namespace
--- It does not matter what you name this "bdsc", "core", "banana".. up to you *(may sound dumb to point that out but ive been asked a few times)*.
local core <const> = exports.bdsc:import()

--- Create and setup a new player instance
RegisterCommand("make", function(src)
    local player = core.create_player(src)
    if not player then print("Failed to create player") return end

    -- Attach initial data
    player:add_data("stats", { health = 100, stamina = 50 }, true) -- if true will replicate to client

    -- Add a method to get player health
    player:add_method("stats", "get_health", function(self)
        local stats = self:get_data("stats")
        return stats and stats.health or 0
    end)

    -- Lifecycle method triggered on save
    player:add_method("stats", "on_save", function()
        print("saving player stats")
    end)

    -- Lifecycle method triggered on destroy
    player:add_method("stats", "on_destroy", function()
        print("destroying player, do something with stats?")
    end)

    print("Player created and method added.")
end, false)

--- Print the players current health
RegisterCommand("hp", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Health:", player:run_method("stats", "get_health"))
end, false)

--- Deal damage to the player by reducing health
--- Run /hp command again after to check if damaged
RegisterCommand("damage", function(src, args)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local amount = tonumber(args[1]) or 10
    local stats = player:get_data("stats")
    stats.health = stats.health - amount
    player:set_data("stats", stats, true)

    print("Damaged player for", amount)
end, false)

--- Add a method to uppercase the job name
RegisterCommand("add_upper", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    player:add_method("get_upper_job", function(self)
        local job = self:get_data("job")
        return job and string.upper(job) or "NONE"
    end)

    print("Method get_upper_job added.")
end, false)

--- Call the uppercased job method
RegisterCommand("call_upper", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Upper Job:", player:run_method("get_upper_job"))
end, false)

--- Assign a job to the player
RegisterCommand("job", function(src, args)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local job = args[1] or "thief"
    player:add_data("job", job, true)
    print("Job set to:", job)
end, false)

--- Remove the players job
RegisterCommand("clearjob", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    player:remove_data("job")
    print("Job removed.")
end, false)

--- Dump all stored data for the player
RegisterCommand("dump", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    local data = player:get_data()
    print("Data dump:")
    for k, v in pairs(data) do
        print(k .. ": " .. json.encode(v))
    end
end, false)

--- Force a manual sync of all replicated data
RegisterCommand("sync", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    player:sync_data()
end, false)

--- Attempt to call a method that doesn't exist
RegisterCommand("badcall", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Calling missing method:")
    local result = player:run_method("nonexistent")
    print("Result:", result or "nil")
end, false)

--- Attempt to write directly to the _data table (should be blocked)
RegisterCommand("overwrite", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Trying to write to _data (should error)...")
    player._data["hack"] = true
end, false)

--- Attempt to read directly from the _data table (should error)
RegisterCommand("read", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Trying to read _data directly (should error)...")
    local value = player._data["stats"]
end, false)

--- Attempt to save player
--- Should print "saving player stats" from the `on_save` method we added in `/make`
RegisterCommand("save", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Trying to save player")
    player:save()
end)

--- Attempt to destroy player
--- Should print "destroying player, do something with stats?" from the `on_destroy` method we added in `/make`
RegisterCommand("save", function(src)
    local player = core.get_player(src)
    if not player then print("No player found.") return end

    print("Trying to destroy player")
    player:destroy()
end)
```

***


# API

The **BOII Development Server Core (BDSC)** exposes a clean, structured API designed to give you controlled access to essential systems - without enforcing how your server should work.

It focuses on **player handling**, **server registry**, and **utility helpers**, giving you full control over your gameplay logic through modular extensions.

***

## What It Covers

The API is split into three main areas:

#### **1. Registry**

Manage the core player lifecycle.

* Create and register player objects
* Retrieve players by source
* Maintain the live player registry

#### **2. Player API**

Interact with a player object via its public-facing interface.

* Add data or methods dynamically
* Sync data to clients when needed
* Trigger save/destroy lifecycle events

#### **3. Utility Functions**

Access core helpers for logging, and translations.

***

## Design Philosophy

BDSC is built for developers who want flexibility without the baggage.

* Clean, minimal API
* Lifecycle and data sync built-in
* Fully extensible from external resources
* No forced framework rules
* No prebuilt gameplay logic

***

## How to Use the API

BDSC exposes its full API via a single namespace import.

#### **Namespace Import**

Use `exports.bdsc:import()` to get access to all available functions and objects:

```lua
local bdsc = exports.bdsc:import()

local player = bdsc.get_player(source)
player:add_data("custom", { foo = "bar" }, true)
```

> All player objects support the full public API, including `add_data`, `run_method`, and `save`.

***

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Player Registry</td><td><a href="/files/p74L34Q7wphkJXWHab48">/files/p74L34Q7wphkJXWHab48</a></td><td><a href="/pages/x3Fl6t9j4MWactzQWlSQ">/pages/x3Fl6t9j4MWactzQWlSQ</a></td></tr><tr><td>Player Object</td><td><a href="/files/KQrHHTGU9BKAX4iXpBCC">/files/KQrHHTGU9BKAX4iXpBCC</a></td><td><a href="/pages/Y5meDzXaD8uaE629pU4y">/pages/Y5meDzXaD8uaE629pU4y</a></td></tr><tr><td>Utility Functions</td><td><a href="/files/4dK946byRmmvxHpcPH1e">/files/4dK946byRmmvxHpcPH1e</a></td><td><a href="/pages/BBgGtpTQvejMJWvFxbKS">/pages/BBgGtpTQvejMJWvFxbKS</a></td></tr></tbody></table>


# Registry

The player registry handles the creation, tracking, and lifecycle of all active player objects in BDSC.

It provides functions to:

* Create and register new players
* Access and remove player objects
* Save player state
* Retrieve client-side replicated data

This system is core to how BDSC manages players internally. \
Use these functions to safely access and interact with player objects.

***

## Server

***

Creates and registers a player object from a source ID.

#### Parameters

* `source`: `number`

#### Returns

* `boolean`

```lua
bdsc.create_player(source)
```

***

### add\_player

Adds a player object to the internal registry.\
Realistically this should never need to be called externally, this is handled internally on object creation.

#### Parameters

* `player`: `table`&#x20;

#### Returns

* `boolean`

```lua
bdsc.add_player(player)
```

***

### remove\_player

Removes a player from the internal registry.

#### Parameters

* `source`: `number`

#### Returns

* `boolean`

```lua
bdsc.remove_player(source)
```

***

### get\_players

Retrieves all currently registered players.

#### Returns

* `table`

```lua
local all_players = bdsc.get_players()
```

***

### get\_player

Retrieves a specific player object from the registry.

#### Parameters

* `source`: `number`

#### Returns

* `table|nil`

```lua
local player = bdsc.get_player(source)
```

***

### save\_players

Calls `:save()`

```lua
bdsc.save_players()
```

***

## Client

***

### get\_player\_data

Retrieves replicated client-side player data.

#### Returns

* `table`

```lua
local data = bdsc.get_player_data()
```


# Player Object

The player object provides a clean public interface for interacting with a specific player in BDSC.

Each player is represented by a dynamically extendable object that supports:

* Accessing and modifying custom data
* Syncing values to the client
* Adding and running namespaced methods
* Lifecycle functions like `save()` and `destroy()`

All logic attached to a player should go through this API — not directly via internal tables.\
Use `bdsc.get_player(source)` to retrieve the player object, then call methods like `player:get_data()` or `player:run_method(...)` to interact with it.

***

## has\_data

Check if the player has a data namespace assigned.

#### Parameters

* namespace: `string`

#### Returns

* `boolean` – True if it exists.

```lua
player:has_data("stats")
```

***

## get\_data

Retrieve data from a specific namespace.

#### Parameters

* namespace: `string`

#### Returns

* `table|any`&#x20;

```lua
local stats = player:get_data("stats")
```

***

## add\_data

Add a new data namespace to the player.

#### Parameters

* namespace: `string`&#x20;
* value: `any`&#x20;
* replicated?: `boolean`&#x20;

#### Returns

* `boolean|nil`

```lua
player:add_data("stats", { health = 100 }, true)
```

***

## remove\_data

Delete an existing data namespace.

#### Parameters

* namespace: `string`&#x20;

```lua
player:remove_data("stats")
```

***

## set\_data

Update values inside an existing namespace (must be a table).

#### Parameters

* namespace: `string`
* updates: `table`
* sync?: `boolean`

#### Returns

* `boolean`

```lua
player:set_data("stats", { health = 80 }, true)
```

***

## sync\_data

Force replication of a namespace to the client.

#### Parameters

* namespace?: `string`

```lua
player:sync_data("stats")
```

***

## update\_user\_data

Update a value inside the persistent user block and propagate it to storage.

#### Parameters

* key: `string`
* value: `any`

```lua
player:update_user_data("rank", "admin")
```

***

## add\_method

Add a new dynamic method to the player.

#### Parameters

* namespace: `string`
* name: `string`&#x20;
* fn: `function(player, ...)`&#x20;

```lua
player:add_method("combat", "take_damage", function(self, amount)
    local stats = self:get_data("stats")
    stats.health = stats.health - amount
end)
```

***

## remove\_method

Remove a method from the player.

#### Parameters

* namespace: `string`
* name: `string`

```lua
player:remove_method("combat", "take_damage")
```

***

## has\_method

Check if a player has a method defined.

#### Parameters

* namespace: `string`
* name: `string`

#### Returns

* `boolean`

```lua
if player:has_method("combat", "take_damage") then
    ...
end
```

***

## run\_method

Execute a method that was added to the player.

#### Parameters

* namespace: `string`
* name: `string`
* ...: any

#### Returns

* `any`&#x20;

```lua
player:run_method("combat", "take_damage", 25)
```

***

## save

Call `on_save()` method for all namespaces if defined.

```lua
player:save()
```

***

## destroy

Triggers `on_destroy()` for all namespaces if defined, saves the player, and unregisters it.

```lua
player:destroy()
```

***


# Utility

BDSC includes a small set of shared utility functions to support logging, translation, and time formatting. \
\
These functions are available globally under the `bdsc` namespace and work in both server and client environments.

They are intentionally minimal, with no external dependencies or unnecessary abstraction.

***

## get\_current\_time

Returns the current system time as a formatted string.

#### Returns

* `string`

```lua
bdsc.get_current_time()
```

***

## log

Prints a debug message to the console if `bdsc.debug_mode` is enabled.\
Supports standard log levels for easier development debugging.

#### Parameters

* level: `string`
* message: `string`

```lua
bdsc.log(level, message)
```

***

## translate

Retrieves a translation string by key and applies optional formatting arguments.\
Falls back to key and values if translation is missing.

#### Parameters

* key: `string`&#x20;
* ...: `any`&#x20;

#### Returns

* `string`

```lua
bdsc.translate(key, ...)
```

***


# Vendors

{% hint style="warning" %}

### Beta Release

Script is currently a BETA release until some customer feedback has been received.

Things may change and be adjusted.
{% endhint %}

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

***


# Guides


# Creating Vendors

Creating new vendors is straight forward.\
Follow the steps below and you will have as many vendors as you want setup in no time at all.

***

## Step 1. Setting Up Locations

The order of the steps does not really matter but for tutorial sake we will start here.&#x20;

Open the `data` folder followed by the `locations.lua` file.\
Here you can define new locations within the module:&#x20;

```lua
return {
    
    some_general_store = {
        type = "general",
        label = "Some General Store",
        coords = vector4(-306.99, -971.24, 31.08, 159.15),
        payment_types = { "balance", "item" },
        can_access = function()
            return true
        end,
    }
    
}
```

#### Options

* **key** - A unique key ID to represent the store.
* **type** - A type for the store, this is important, it connects with your items.
* **label** - A readable label for UI display.
* **coords** - Coordinates to spawn the store ped and display blip if enabled, this is also used for server side validation.
* **payment\_type** - The types of payment the vendor will accept.
  * **balance** - Any balance type for frameworks; "cash", "crypto", "bank", etc.
  * **item** - Any item registered in your server, trade bread for pistols if you really want.
* **can\_access** - Function used for adding custom restrictions to open the vendor.

#### Restricting Vendor Access

You can restrict access to a vendor by utilising the `can_access` function.\
Here is a quick example of restricting a vendor so players cannot access it from within a vehicle.

```lua
can_access = function()
    return not IsPedInAnyVehicle(PlayerPedId())
end
```

***

## Step 2. Setting Up Peds

Once you have setup a location and decided what `type` you are using for your vendors, open the `peds.lua` file and create an entry for your new vendor type:

```lua
return {

    general = {
        model = "mp_m_shopkeep_01",
        scenario = "WORLD_HUMAN_AA_COFFEE",
        networked = false  
    }
    
}
```

#### Options

* **key** - The  connects to your vendor type created in Step 1.
* **model** - The ped model to spawn.
* **scenario** - The world scenario to run on the ped.
* **networked** - Network flag for ped spawning

***

## Step 3. Setting Up Blips

The next step is setting up your blip for the vendor:

```lua
return { 

    general = {
        label = "General Store",
        sprite = 52, 
        colour = 0,
        scale = 0.6,
        enabled = true
    }
    
}
```

#### Options

* **key** - The  connects to your vendor type created in Step 1.
* **label** - A readable label for UI display.
* **sprite** - ID of sprite to use.
* **colour** - Colour for sprite.
* **scale** - The scale of blip on the map.
* **enabled** - If false blip will not be added to locations.

***

## Step 4. Setting Up Items

Now the main sauce of the script.\
You can set any items you want, define whatever prices you want, using any money type or other item you want.

You can build up your items list like so:

```lua
return {

    test_item = {
        label = "Test Item",
        image = "test_item.png",
        description = "This is just an example test item.",
        locations = { "general", "medical" },
        categories = { "test", "food" },
        stock = 500,
        prices = {
            cash = { type = "balance", value = 5 },
            bank = { type = "balance", value = 10 },
            crypto = { type = "balance", value = 2 },

            bread = { type = "item", value = 5 },
            water = { type = "item", value = 5 }
        }
    }

}
```

Options

* **key** - Unique identifier for the item.
* **label** - A readable label for UI display.
* **description** - A readable description for the item.
* **locations** - The vendor types the item can be shown in, e.g. this item could be purchased from "general" or "medical" vendors.
* **categories** - Categories to place the item in, any unique categories will generate the UI page tabs.
* **stock** - The amount of stock the item has this is refreshed every 5 minutes by 25% on default settings.
* **prices** - The prices the item can be purchased for
  * **key** - Unique item or balance for your server
  * **type** - The money type players can purchase for
    * **balance** - Any framework balance type; "cash", "crypto", "bank" etc.
    * **item** - Any registered item in your server.
  * **value** - The price of the item

Thats it now your good to go!

***

## Summary

* Create a new location in `locations.lua`&#x20;
* Setup blips and peds for the vendor type in `blips.lua` and `peds.lua`&#x20;
* Define your items within `items.lua`&#x20;
* Restart and go.

***


# Installation

Getting setup with the Vendor script is straight forward.\
Just follow the steps below and you'll be selling your nan's favourite cat in no time.

***

{% stepper %}
{% step %}

### Download

Once you have purchased your resource download it from **Keymaster**
{% endstep %}

{% step %}

### Adding The Resource

After downloading the resource extract and add it into your server resources.
{% endstep %}

{% step %}

### Ensure It

Add `ensure vendors` to your `server.cfg`&#x20;
{% endstep %}

{% step %}

### Downloading Dependencies

Get the latest release versions of our dependency resources if you do not already have them:

* [**BOII Development Tool Kit**](https://github.com/boiidevelopment/bdtk/releases)
* [**BOII Development UI Kit**](https://github.com/boiidevelopment/bduk/releases)
  {% endstep %}

{% step %}

### Adding The Dependencies

Add the `bdtk` & `bduk` resource into your server, and ensure them.\
Your order should be like so:

```
ensure bduk
ensure bdtk # must be started before vendors
ensure vendors
```

{% endstep %}

{% step %}

### Resource Configuration

For resource configuration read the following guide: [**Creating Vendors**](/fivem-paid-resources/vendors/guides/creating-vendors)
{% endstep %}

{% step %}

### Restart Your Server

Restart your server and you will be good to go, start buying guns for beans, or fake ids for empty bottles... skies the limit.
{% endstep %}
{% endstepper %}


# Configuration

Script configuration boils down to a small set of `convars` and static data files.\
Convars overrides can be set in your `server.cfg` or you can leave them as default.&#x20;

Defining new locations, items, blips and peds can be done through the `data` files.\
For more on this read [**Creating Vendors**](/fivem-paid-resources/vendors/guides/creating-vendors).

***

## Convar Settings

You can override the default settings of these convars within your `server.cfg` for more about convars read: [**FiveM Docs - Convars**](https://docs.fivem.net/docs/scripting-reference/convars/).

```lua
vendors.language = GetConvar("vendors:language", "en")
vendors.locale = exports.bdtk:get("locales." .. vendors.language, true) or {}

vendors.image_file_path = GetConvar("vendors:image_file_path", "nui://vendors/images/")

vendors.debug_mode = GetConvar("vendors:debug_mode", "false") == "true"
vendors.debug_colours = {
    reset = "^7",
    debug = "^6",
    info = "^5",
    success = "^2",
    warn = "^3",
    error = "^8",
    critical = "^1",
    dev = "^9"
}

vendors.restock_time = GetConvar("vendors:restock_time", "5")
vendors.restock_percentage = GetConvar("vendors:restock_percentage", "25")

vendors.force_dui = GetConvar("vendors:force_dui", "false") == "true"
vendors.target = not vendors.force_dui and (GetResourceState("ox_target") == "started" and "ox_target" or GetResourceState("qb-target") == "started" and "qb-target") or nil

```

***


# List Inventory

{% hint style="danger" %}

### BETA RELEASE

This is currently a **BETA** release, some issues are to be expected but they will be fixed asap!\
Any additional systems requested can be added down the line. \
\
If you have purchased the **BETA**, you're appreciated :heart:
{% endhint %}

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

**A flashback to inventory systems that made sense — now modernized, modularized, and way less ugly.**

**Not a Grid. Not a Puzzle. Just an Inventory.**

Built using BDUK’s flexible card UI - think old-school MMOs with 2025 - level extensibility. \
It’s everything you liked about classic inventories, without the trash fires.

***

## What Is This Inventory?

A clean, fully modular inventory system that works out of the box with:

* Player inventories
* Containers (trunks, fridges, drops, whatever)
* Item rarities
* Item degradation
* Containers providing quality preservation
* Stack splitting & transfers
* Props, animations, and usable item logic
* Weapon handling with ammo + attachments
* Weapon serial codes
* Weapon allow-list and clearing

Oh, and it looks good too. Because we're not savages.

***

## Who It’s For

It was built mainly as a working project to learn about inventory systems and provide a different take on to inventory scene, so for anyone tired of inventory systems that:

* Fight you at every turn
* Are bolted onto old RP frameworks
* Have 8000 exports to transfer a sandwich

If you’re building a survival system, hardcore PvP loot loop, or just want something actually usable without 40 dependencies - this is it.

#### You get:

* Simple list-based logic (no weight shenanigans)
* Clean object-oriented design
* Proper registry-based item actions
* A working item degradation system (yes, actually works)
* Support for quick use, props, tooltips, and serials

***

## What It Supports

* **Player + Container Inventories**\
  Uses factory-based logic and clean public/private method splitting.\
  Containers include drops, trunks, gloveboxes, fridges, lockers, etc.
* **Modular Actions**\
  Each item can have custom `use`, `modify`, whatever.\
  Global actions include:
  * Use
  * Drop (full/partial)
  * Split
  * Transfer (all/amount)
  * Modify (attachments, plus more soon..)
* **Attachments & Weapons**\
  Weapons support:
  * Serial numbers
  * Ammo types (via items)
  * Attachments (client component sync)
  * Persistent state per item
* **Item Degradation**\
  Items can lose quality over time - some containers block degrade to preserve items (like fridges).\
  That moldy sandwich? Yeah, it’s coming for you.
* **Stylized Tooltip Cards**\
  Hover. Flex. Cry at the quality stat slowly ticking down.
* **BDUK UI Integration**\
  Uses the full power of the modal and card system. Minimal performance cost. Max vibes.

***

## Structure

```bash
list_inventory/
│
├── core/
│   ├── actions/          # All transferable/useable logic: use, drop, split, transfer, modify
│   ├── containers/       # Container logic: creation, syncing, methods
│   ├── lib/              # Utility functions (slots, item formatting)
│   ├── player/           # Player inventory logic
│   ├── client.lua        # UI and client-side actions
│   ├── degrade.lua       # Quality / durability logic
│   └── registry.lua      # Item registry (on_use hooks etc.)
│
├── data/                 # Preloaded definitions (items, containers, vehicles)
├── images/               # Item icons
├── locales/              # i18n (default: en.lua)
├── wrappers/             # Framework support (QBCore, bdsc, + more asap)
├── fxmanifest.lua        # Manifest
├── install.sql           # Install schema
└── init.lua              # Boot logic and base configuration
```

***

## Quick Install

No 300-step wizard. Just drop and go.

1. Drop into your resources folder
2. Ensure your framework wrapper is configured (`qb.lua`, `bdsc.lua`)
3. `ensure list_inventory` in your server.cfg
4. Run the included SQL file
5. Enjoy.

***

## Why List, Though?

Grid inventories are fine… if you like visual Sudoku every time you pick up a bottle of water.

This isn’t for that.

`list_inventory` is:

* **Fast to use**
* **Easy to extend**
* **Good for gameplay loops where layout doesn’t matter**
* **Still sexy enough to feel modern**

Want a grid or slot inventory? Go write one *(or wait for ours)*. \
Want to *play* your game? Use this.

***

## Notes

This is a **beta** release. That means:

* There *will* be edge cases.
* We *might* bully you if you ignore the docs.
* But we’re also here to fix bugs and improve it.

This isn’t a toy system — it’s powering survival, PvP, and loot-heavy servers already.\
You get full source access. Extend it. Break it. Fork it. Just don’t complain when your fridges explode because you renamed a method called `degrade_item`.


# Guides

These guides cover how to get started using the list inventory system from core functionality like creating containers and adding items, to more advanced setups like custom items, weapon logic, and integration with vehicles.

Whether you're building a survival system, a loot-based PvP mode, or just want clean inventory logic — start here.

Use the cards below to dive into each guide.

***

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Creating &#x26; Using Containers</td><td><a href="/files/LPciOP66eipXdqlq5QWC">/files/LPciOP66eipXdqlq5QWC</a></td><td><a href="/pages/iZKfMe2Fysue5LfvEu0U">/pages/iZKfMe2Fysue5LfvEu0U</a></td></tr><tr><td>Creating Items</td><td><a href="/files/zcj7sPMp1vekeVxf226W">/files/zcj7sPMp1vekeVxf226W</a></td><td><a href="/pages/ya0wpRE8wyv9rVZUWaNY">/pages/ya0wpRE8wyv9rVZUWaNY</a></td></tr><tr><td>Customising Vehicle Inventories</td><td><a href="/files/NLCpFC68DmhT03aLvATr">/files/NLCpFC68DmhT03aLvATr</a></td><td><a href="/pages/7U9Nc380iWCcGTbhAXVz">/pages/7U9Nc380iWCcGTbhAXVz</a></td></tr></tbody></table>


# Creating & Using Containers

Containers are shared, slot-based inventories that can be created dynamically or pre-defined for vehicles, world drops, fridges, etc. They support the same clean method API as player inventories.

***

## Creating a Container

Use the `create_container` export **from the server** to create any container:

```lua
local data, err = exports.list_inventory:create_container("world", "fridge", {
    owner = "house_7",
    coords = vector3(123.4, 456.7, 789.0),
    persist = true
})
```

#### Params

| Name       | Type     | Description                                  |
| ---------- | -------- | -------------------------------------------- |
| `category` | `string` | Logical group (`world`, `vehicle`, etc.)     |
| `subtype`  | `string` | Container type (`fridge`, `drop`, etc.)      |
| `options`  | `table`  | Metadata: `owner`, `coords`, `persist`, etc. |

***

## Getting a Container

Use `get_container(id)` to retrieve an active container object:

```lua
local container = exports.list_inventory:get_container("trunk:ABC123")
if not container then return end
```

Once retrieved, you can use any container method:

```lua
container:add_item("bandage", 2)
```

***

## Container Methods

All container objects support these public methods:

* `add_item(id, amount, metadata)`
* `remove_item(lookup, amount)`
* `split_item(slot, amount)`
* `get_items()` / `get_item(lookup)`
* `has_item(lookup, amount)`
* `clear_items()`
* `save()` / `sync()`
* `get_data(key)` / `set_data(key, value)`

***

## Default Container Types

Container types and defaults are defined in `data/containers.lua`. You can modify these or add your own.

#### Vehicles

| Type       | Default Slots | Category  |
| ---------- | ------------- | --------- |
| `trunk`    | 100           | `vehicle` |
| `glovebox` | 10            | `vehicle` |
| `trailer`  | 120           | `vehicle` |

#### Loot & Drops

| Type      | Default Slots | Category | Extras                    |
| --------- | ------------- | -------- | ------------------------- |
| `drop`    | 200           | `loot`   | Bag prop, outlines red    |
| `airdrop` | 50            | `loot`   | Crate prop, outlines blue |

#### Storage Containers

| Type          | Default Slots | Category  | Notes                       |
| ------------- | ------------- | --------- | --------------------------- |
| `desk_fridge` | 5             | `storage` | quality\_preservation = 1.5 |
| `mini_fridge` | 40            | `storage` | quality\_preservation = 2.0 |
| `fridge`      | 40            | `storage` | prevent\_spoil = true       |

You can define props, outline styles, and special logic like spoilage blocking or preservation multipliers.

***

## Temporary Drops

Drop containers (category `loot`, subtype `drop`) are temporary. When emptied, they are:

* Deleted from server memory
* Removed from the client UI

These are handled internally when a player drops an item however you could make one if you want too, why not? Who said you cant.

```lua
local coords = GetEntityCoords(GetPlayerPed(source))
exports.list_inventory:create_container("loot", "drop", {
    owner = "drop_" .. source,
    coords = coords,
    persist = false
})
```

***

## Notes

* Containers use **slot limits**, not weights
* Some types (like fridges) can prevent degradation or slow it if enabled
* If a persistent container already exists for an `owner`, it will be loaded instead of recreated
* You can define **your own** container types freely, provided ones are purely example

Stay slotted, stay chill.™


# Creating Items

Items are defined via a shared `item_list` table, stored in `data/items.lua`. \
Each item is fully data-driven and supports data, actions, props, degradation, attachments, and more.

Below are fully built examples covering the major item types:

***

{% hint style="warning" %}
Statuses support is coming very soon please bare rebuilding another system currently, once complete a `modifiers.statuses = {}` section will be added.&#x20;
{% endhint %}

## Consumable Items

A stackable consumable item with an animation and prop attachment.

```lua
water = {
    id = "water",
    label = "Water",
    description = { "A refreshing bottle of clean water.." },
    image = "water.png",
    stackable = 10,
    data = {
        rarity = "common",
        degrade_rate = 0.25,
        quality = 100
    },
    actions = {
        use = {
            animation = {
                progress = {
                    type = "circle",
                    message = "Drinking Water..",
                    segments = 30,
                    gap = 3
                },
                dict = "mp_player_intdrink",
                anim = "loop_bottle",
                flags = 49,
                duration = 5000,
                freeze = false,
                continuous = false,
                props = {
                    {
                        model = "ba_prop_club_water_bottle",
                        bone = 60309,
                        coords = { x = 0.0, y = 0.0, z = -0.05 },
                        rotation = { x = 0.0, y = 0.0, z = 0.0 },
                        is_ped = true,
                        sync_rot = true
                    }
                },
                callback = function(_src, slot)
                    TriggerEvent("list_inventory:sv:remove_item", _src, slot)
                end
            }
        },
        drop = true
    }
}
```

***

## Ammo Items

Ammo items store bullet count and are used to reload compatible weapons.

```lua
ammo_pistol = {
    id = "ammo_pistol",
    label = "Pistol Ammo",
    description = { "A clip of pistol ammo" },
    image = "ammo_pistol.png",
    data = {
        rarity = "common",
        ammo_count = 12
    },
    actions = {
        use = function(source, slot, id, def)
            add_ammo_to_weapon(source, slot, id, def)
        end,
        drop = true
    }
}
```

***

## Weapons

Weapons track serials, ammo, durability, and can be modified with attachments.\
Serials are automatically assigned to weapons if one does not already exist.

```lua
weapon_pistol = {
    id = "weapon_pistol",
    label = "Pistol",
    description = {
        "A standard semi-automatic 9mm handgun.",
        "Can be purchased from any ammunation."
    },
    image = "weapon_pistol.png",
    data = {
        rarity = "common",
        serial = "", -- assigned on add_item
        ammo = 0,
        ammo_types = { "ammo_pistol" },
        attachments = {},
        degrade_rate = 0.25,
        durability = 100
    },
    actions = {
        use = function(source, slot, id, def)
            use_weapon(source, slot, id, def)
        end,
        modify = true,
        drop = true
    }
}
```

***

## Weapon Attachments

Attachments use `modifiers.attachments` to define compatibility with weapons and GTA components.

```lua
pistol_mag_extended = {
    id = "pistol_mag_extended",
    label = "Extended Mag: Pistol",
    description = { "Extended magazine for supported 9mm pistols." },
    image = "pistol_mag_extended.png",
    data = {
        rarity = "rare"
    },
    modifiers = {
        attachments = {
            { weapon = "weapon_pistol", component = "COMPONENT_PISTOL_CLIP_02" },
            { weapon = "weapon_heavypistol", component = "COMPONENT_HEAVYPISTOL_CLIP_02" }
        }
    },
    actions = {
        drop = true
    }
}
```

***

## Notes

* Use `data` to define any item-specific metadata (quality, ammo, serials, etc.)
* `actions.use` supports either a full animation table or a function
* Use `modify = true` on weapon actions to enable the modification UI
* Attachments are matched to weapons using `modifiers.attachments`
* Avoid mixing `quality` and `durability` in a single item (visual conflict)
* Clothing as items & consumables status modifying coming **very soon**

This structure supports **deep customization** with minimal boilerplate. New item types can be added by following these patterns.

Want to define a new item? Just copy one of these and tweak the fields.


# Customising Vehicle Inventories

Vehicle inventories (trunk + glovebox) are automatically assigned based on vehicle class or model. \
You can override defaults for specific vehicles, or rely on fallback class-based sizes.

***

## Defaults (By Class)

If a specific vehicle model is **not defined**, the system will fall back to these default sizes:

| Class        | Trunk | Glovebox |
| ------------ | ----- | -------- |
| `compact`    | 60    | 4        |
| `sedan`      | 70    | 6        |
| `suv`        | 80    | 6        |
| `coupe`      | 60    | 4        |
| `muscle`     | 70    | 5        |
| `sports`     | 50    | 5        |
| `super`      | 40    | 4        |
| `motorcycle` | 16    | 2        |
| `offroad`    | 90    | 6        |
| `industrial` | 120   | 6        |
| `utility`    | 100   | 6        |
| `van`        | 110   | 6        |
| `service`    | 90    | 6        |
| `emergency`  | 100   | 8        |
| `military`   | 130   | 10       |
| `commercial` | 120   | 8        |

***

## Custom Model Overrides

To override default inventory for a **specific vehicle**, just define it directly by model name:

```lua
adder = {
    trunk = 35,
    glovebox = 10
}
```

This will override both the class defaults and any fallback behavior.

***

## Notes

* All values are **slot-based**, not weight-based.
* Containers are created automatically when you access the vehicle trunk or glovebox.
* Custom sizes allow better balancing for loot-heavy or RP-specific vehicles.
* Do have plans to add in a `trailer` type for vehicles, soon as I get round to it, if people want it.

To modify these, just update `data/vehicles.lua` with new entries or changes.


# API

The API documentation covers two main API sections:

#### Player

This section covers all available methods for working with player inventories.

* Add / remove / split items
* Get or check item presence
* Sync and save player data
* Use metadata, durability, attachments, and more

#### Container

This section handles logic for world containers, such as:

* Loot drops
* Vehicle trunks
* Fridges or persistent storage

Supports all standard inventory functions:

* Add, remove, split, sync
* Metadata support
* Optional persistence to MySQL

***

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td><a href="/files/KQrHHTGU9BKAX4iXpBCC">/files/KQrHHTGU9BKAX4iXpBCC</a></td><td><a href="/pages/AAegA9IM9ROjp8WPVEt6">/pages/AAegA9IM9ROjp8WPVEt6</a></td></tr><tr><td></td><td><a href="/files/LPciOP66eipXdqlq5QWC">/files/LPciOP66eipXdqlq5QWC</a></td><td><a href="/pages/0wmBwonz10mHjl4FVuC0">/pages/0wmBwonz10mHjl4FVuC0</a></td></tr></tbody></table>


# Player

Before using any **player inventory methods** (`add_item`, `has_item`, `get_item`, etc.), you **must first retrieve the player inventory object** using the provided export:

```lua
local player = exports.list_inventory:get_player(source)
```

This returns the full player inventory object, with all public methods attached.\
Then you can use the methods below like so:&#x20;

```lua
RegisterCommand("give_bread", function(source)
    local player = exports.list_inventory:get_player(source)
    if not player then 
        print("Inventory not found") 
        return 
    end

    player:add_item("bread", 1)
end)
```

***

## create\_player\_inventory

Create a player inventory object and attach methods/data.

#### Params

* `source` (number): The player source

#### Returns

* Player inventory object or `false`

```lua
local player = exports.list_inventory:create_player_inventory(source)
```

***

## get\_player

Returns the inventory object for the given player.

#### Params

* `source` (number): The player source

#### Returns

* Player inventory object or `nil`

```lua
local inv = exports.list_inventory:get_player(source)
```

***

## add\_item

Adds an item to the inventory, optionally with metadata.

#### Params

* `id` (string): Item ID
* `amount` (number): Quantity
* `item_data` (table, optional): Item metadata (e.g., quality, serial)

#### Returns

* `true` on success, or `false, reason` on failure

```lua
player:add_item("water", 2, { quality = 100 })
```

***

## remove\_item

Removes item(s) by slot, ID, or metadata.

#### Params

* `lookup` (number|string|table): Slot number, item ID, or metadata table
* `amount` (number): Amount to remove

#### Returns

* `true` on success, `false` otherwise

```lua
player:remove_item(1, 1) -- slot
player:remove_item("bread", 1) -- ID
player:remove_item({ quality = 50 }, 1) -- metadata
```

***

## has\_item

Checks if inventory contains item by slot, ID, or metadata.

#### Params

* `lookup` (number|string|table): Slot number, item ID, or metadata table
* `amount` (number): Amount required (default: 1)

#### Returns

* `true` if found, otherwise `false`

```lua
player:has_item("water", 2)
```

***

## get\_item

Returns the first matching item by slot, ID, or metadata.

#### Params

* `lookup` (number|string|table): Slot number, item ID, or metadata table

#### Returns

* Item object or `nil`

```lua
local item = player:get_item("water")
```

***

## get\_items

Returns a table of all items in the inventory.

#### Returns

* `table`: All items indexed by slot

```lua
local items = player:get_items()
```

***

## set\_data

Sets a custom data field on the inventory.

#### Params

* `key` (string): Field name
* `value` (any): Value to store

#### Returns

* `true`

```lua
player:set_data("weight", 500)
```

***

## get\_data

Gets a specific or all data values from the inventory.

#### Params

* `key` (string, optional): Field name

#### Returns

* Value of the field or full data table

```lua
local weight = player:get_data("weight")
```

***

## has\_data

Checks whether a custom data key exists.

#### Params

* `key` (string): Field name

#### Returns

* `true` if exists, `false` otherwise

```lua
if player:has_data("slots") then ... end
```

***

## update\_item\_data

Updates the metadata for a specific slot.

#### Params

* `slot` (number|string): Slot number
* `new_data` (table): New metadata to merge in

#### Returns

* `true` on success, `false` on failure

```lua
player:update_item_data(1, { durability = 80 })
```

***

## split\_item

Splits a stack in one slot into a new one.

#### Params

* `slot` (number|string): Source slot
* `amount` (number): Amount to split

#### Returns

* `true` on success, `false` otherwise

```lua
player:split_item(1, 3)
```

***

## clear\_items

Removes all items from the inventory.

#### Returns

* `true`

```lua
player:clear_items()
```

***

## save

Saves the current inventory state to the database.

#### Returns

* `true` on success, `false` on failure

```lua
player:save()
```

***

## sync

Syncs the inventory with the client.

```lua
player:sync()
```


# Containers

Before using any **container methods** (`add_item`, `remove_item`, `has_item`, etc.), you must first retrieve the container object using the following export:

```lua
local container = exports.list_inventory:get_container(id)
```

This returns the active container object for the given `id`, complete with all public methods.\
Then you can use the methods below on the container like so:

```lua
local container = exports.list_inventory:get_container("some_container_id")

if not container then 
    print("No container found for that ID")
    return 
end

container:add_item("bandage", 2)
```

***

## get\_data

Get a specific field or the full container data.

#### Params

* `key` (string, optional): The key to fetch

#### Returns

* Value of the key or full data table

```lua
local coords = container:get_data("coords")
```

***

## set\_data

Set a custom field on the container.

#### Params

* `key` (string): Field name
* `value` (any): Value to store

#### Returns

* `true`

```lua
container:set_data("custom_label", "Loot Crate")
```

***

## get\_items

Returns a table of all items in the container.

#### Returns

* `table`: All container items indexed by slot

```lua
local items = container:get_items()
```

***

## get\_item

Returns the first matching item by slot, ID, or metadata.

#### Params

* `lookup` (number|string|table): Slot number, item ID, or metadata table

#### Returns

* Item object or `nil`

```lua
local item = container:get_item("bandage")
```

***

## has\_item

Checks if container contains an item by slot, ID, or metadata.

#### Params

* `lookup` (number|string|table): Slot number, item ID, or metadata table
* `amount` (number): Amount required (default: 1)

#### Returns

* `true` if found, otherwise `false`

```lua
if container:has_item("weapon_pistol", 1) then ... end
```

***

## add\_item

Adds an item to the container.

#### Params

* `id` (string): Item ID
* `amount` (number): Quantity
* `item_data` (table, optional): Item metadata (e.g., quality, custom props)

#### Returns

* `true` on success, or `false, reason` on failure

```lua
container:add_item("ammo_pistol", 24)
```

***

## remove\_item

Removes item(s) by slot, ID, or metadata.

#### Params

* `lookup` (number|string|table): Slot number, item ID, or metadata table
* `amount` (number): Amount to remove

#### Returns

* `true` on success, `false` otherwise

```lua
container:remove_item("bandage", 2)
```

***

## split\_item

Splits a stack in one slot into a new slot.

#### Params

* `slot` (number|string): Source slot
* `amount` (number): Amount to split

#### Returns

* `true` on success, `false` otherwise

```lua
container:split_item(1, 5)
```

***

## clear\_items

Removes all items from the container.

#### Returns

* `true`

```lua
container:clear_items()
```

***

## save

Persists the container to the database (if marked as persistent).

#### Returns

* `true` on success, `false` on failure

```lua
container:save()
```

***

## sync

Sends container inventory state to all clients.

```lua
container:sync()
```


# Planting System

{% hint style="danger" %}

### BETA RELEASE

Resource is currently a BETA release.\
It will be moved away from this as soon as some feedback has been received.&#x20;
{% endhint %}

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

A planting and growing system that actually feels like part of a game not just a background timer with a leaf prop on it.

Players plant seeds. \
They water them, feed them, cure them, and eventually harvest crops.\
\
It works in survival. \
It works in roleplay. \
It works in your weird PvEvRP crafting wasteland.&#x20;

No spaghetti timers. \
No bloated “greenhouse frameworks.” \
Just a sane, configurable system for growing, harvesting, and enabling your virtual agriculture dreams.

***

## Who It’s For

#### Anyone building:

* Survival systems with food loops
* Farming jobs that aren’t just "press E to get paid"
* Drug production (yes, you can grow weed with it if you want)
* Anything where players interact with the environment and want stuff to grow over time

#### Built for people who:

* Want plants that change, not just sit there
* Need growth systems that respect environment (altitude, weather, soil)
* Like reward balancing (yield, XP, condition chances)
* Hate systems that make you duplicate 400 lines to add a single tomato

***

## What It Does

* Plant seeds, grow crops, harvest rewards
* Water, fertilize, cure, repeat
* Dynamic model stages with thresholds
* Supports pests and diseases with progressive effects
* Environmental modifiers (altitude, weather, ground type)
* Yield tuning (min/max, seed drop chance, etc)
* XP rewards for every action
* Optional item dependencies (shears, watering cans, fertilizer)
* DUI or target-based interaction support
* Fully data-driven. Define 1 plant or 50 — no extra logic needed.

***

## What It Doesn’t Do

* Doesn’t force you into a farming simulator
* Doesn’t care what framework core you're running
* Doesn’t hardcode map zones or interaction logic
* Doesn’t blow up if someone forgets to water once
* Doesn’t assume every crop is weed

***

## Quick Setup

1. Drop into your server
2. Add or adjust plant/pest/disease definitions
3. Trigger interactions via DUI or targeting
4. Watch stuff grow

No bizarre state machines. No obscure exports. Just simple straight forward code.

***

## In Summary

If you want players to actually **plant** and **grow** things and not just spam use a bush, this is for you.\
\
It’s fully configurable. \
It plays nice with your server. \
And it makes planting not suck as much.. maybe...


# Guides

This section covers everything you need to configure, extend, and actually use the growing system.

Whether you're adding new crops, tweaking yield balance, introducing pests, or tying it into your own survival or economy systems these guides break it down by topic.

***

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Adding Plants</td><td><a href="/files/cZNCydEyjjGk9Q8CQJ7B">/files/cZNCydEyjjGk9Q8CQJ7B</a></td><td><a href="/pages/lYhwtZFi7e1r2cJRaZ7y">/pages/lYhwtZFi7e1r2cJRaZ7y</a></td></tr><tr><td>Adding Pests &#x26; Diseases</td><td><a href="/files/Zf9823LvEC8JkyR861EI">/files/Zf9823LvEC8JkyR861EI</a></td><td><a href="/pages/VZEBvQ0fI5A3EpHFNedw">/pages/VZEBvQ0fI5A3EpHFNedw</a></td></tr><tr><td>Adjusting Environment</td><td><a href="/files/KAcpbxZLC7zNu039waZe">/files/KAcpbxZLC7zNu039waZe</a></td><td><a href="/pages/LzvyGZ6u9lDIMtDnDcZ4">/pages/LzvyGZ6u9lDIMtDnDcZ4</a></td></tr></tbody></table>


# Adding Plants

This guide covers how to define new plant types in the `data/plants.lua` static configuration file.&#x20;

Each plant entry defines how that plant behaves in the growing system covering growth stages, item requirements, XP rewards, environmental resistances, and yield.

***

## Default Values

The `defaults` table defines shared values that are inherited by all plants unless explicitly overridden.

#### Example: `defaults`

```lua
plants.defaults = {
    image = "plant_icon.png",
    should_outline = true,
    outline_colour = { r = 100, g = 200, b = 100 },
    preferred_altitude = { min = 0, max = 2000 },

    stages = {
        { stage = 1, model = "prop_veg_crop_03_cab", threshold = 0 },
        { stage = 2, model = "prop_veg_crop_03_cab", threshold = 25 },
        { stage = 3, model = "prop_veg_crop_03_cab", threshold = 50 },
        { stage = 4, model = "prop_veg_crop_03_cab", threshold = 75 },
        { stage = 5, model = "prop_veg_crop_03_cab", threshold = 100 }
    },

    resistances = {
        altitude = 1,
        disease = 1,
        pest = 1,
        soil = 1,
        weather = 1
    },

    yield = {
        min = 2,
        max = 6,
        seed_chance = 0.2,
        seed_factor = 0.2
    },

    xp = {
        water = { min = 2, max = 6 },
        fertilize = { min = 2, max = 6 },
        harvest = { min = 3, max = 9 },
        plant = { min = 1, max = 4 },
        cure = { min = 1, max = 4 }
    },

    water_item = { id = "watering_can_full", label = "Watering Can (Full)", min = 10, max = 20 },
    fertilizer_item = { id = "fertilizer", label = "Basic Fertilizer", min = 5, max = 10 },
    harvest_item = { id = "trimming_shears", label = "Trimming Shears" }
}
```

***

## Adding a New Plant

Each plant is defined as a key in the returned table. All properties are optional and will fall back to `defaults` if omitted.

#### Example: Cabbage

```lua
plants.cabbage = {
    label = "Cabbage",
    image = "cabbage.png",
    xp = {
        water = { min = 2, max = 5 },
        fertilize = { min = 2, max = 5 },
        harvest = { min = 4, max = 8 },
        plant = { min = 2, max = 4 },
        cure = { min = 1, max = 3 }
    }
}
```

Only XP values and image are overridden. All other values will use the shared `defaults`.

#### Example: Tomato with Custom Stages

```lua
plants.tomato = {
    label = "Tomato",
    image = "tomato.png",
    stages = {
        { stage = 1, model = "prop_veg_crop_01", threshold = 0 },
        { stage = 2, model = "prop_veg_crop_01", threshold = 25 },
        { stage = 3, model = "prop_veg_crop_01", threshold = 50 },
        { stage = 4, model = "prop_veg_crop_01", threshold = 75 },
        { stage = 5, model = "prop_veg_crop_01", threshold = 100 }
    }
}
```

This overrides the stage models but inherits everything else.

***

## Automatic Rewards

Each plant automatically generates two reward items unless manually defined:

* A crop item: `{id = "<plant>_crop", label = "<Label>"}`
* A seed item: `{id = "<plant>_seed", label = "<Label> Seed"}`

These are stored in the `rewards` field per plant:

```lua
plants.tomato.rewards = {
    crop = { id = "tomato_crop", label = "Tomato" },
    seed = { id = "tomato_seed", label = "Tomato Seed" }
}
```

This is handled automatically at the bottom of the file:

```lua
for crop_name, def in pairs(plants) do
    if crop_name ~= "defaults" then
        def.rewards = def.rewards or {}
        def.rewards.crop = { id = crop_name .. "_crop", label = def.label }
        def.rewards.seed = { id = crop_name .. "_seed", label = def.label .. " Seed" }
    end
end
```

You can override this if needed by providing your own `rewards` table.

***

## Plant Definition Reference

| Field                | Description                                                             |
| -------------------- | ----------------------------------------------------------------------- |
| `label`              | Display name of the plant.                                              |
| `image`              | Icon filename shown in the UI.                                          |
| `stages`             | Table of `{ stage, model, threshold }` entries defining growth visuals. |
| `resistances`        | Environmental tolerance scores (1 = average).                           |
| `yield`              | Harvest quantity and seed drop logic.                                   |
| `xp`                 | XP rewards granted for various interactions.                            |
| `water_item`         | Item used to water the plant (requires `id`, `label`, `min`, `max`).    |
| `fertilizer_item`    | Item used to fertilize the plant.                                       |
| `harvest_item`       | Required item to harvest the crop.                                      |
| `preferred_altitude` | Optional altitude range the plant prefers to grow in.                   |
| `should_outline`     | If true, highlights the plant with an outline in DUI-based UIs.         |
| `outline_colour`     | RGB values used for the outline color.                                  |
| `rewards`            | Custom crop and seed item definitions (optional).                       |


# Adding Pests & Diseases

Pests and diseases are defined in static Lua files under `data/`.&#x20;

They apply negative effects to specific plant types and progress over time unless cured. \
These definitions are used by the growing system to apply random or conditional challenges to plants.

***

## Structure: Pests

A pest definition must include the following fields:

| Field           | Description                                                             |
| --------------- | ----------------------------------------------------------------------- |
| `plant_types`   | Array of plant keys this pest can affect (e.g. `"cabbage"`, `"tomato"`) |
| `label`         | Human-readable name                                                     |
| `severity`      | Optional severity label (informational only)                            |
| `effects`       | Tiered effect values applied per severity (`low`, `medium`, `high`)     |
| `cure`          | Required item to cure the pest (with `id`, `label`, and `amount`)       |
| `increase_rate` | How fast the severity escalates (e.g. ticks until next stage)           |

#### Example: `cabbage_loopers`

```lua
cabbage_loopers = {
    plant_types = { "cabbage", "tomato", "corn" },
    label = "Cabbage Loopers",
    severity = "low",
    effects = {
        low = { quality = 2, health = 1 },
        medium = { quality = 4, health = 3 },
        high = { quality = 6, health = 5 }
    },
    cure = { id = "organic_pesticide", label = "Organic Pesticide", amount = 1 },
    increase_rate = 4
}
```

***

## Structure: Diseases

Diseases follow the exact same structure and fields as pests.

#### Example: `downy_mildew`

```lua
downy_mildew = {
    plant_types = { "cabbage", "tomato", "corn" },
    label = "Downy Mildew",
    severity = "low",
    effects = {
        low = { quality = 2, health = 1 },
        medium = { quality = 4, health = 2 },
        high = { quality = 6, health = 3 }
    },
    cure = { id = "copper_fungicide", label = "Copper Fungicide", amount = 1 },
    increase_rate = 5
}
```

***

## Effects Table

The `effects` table defines what penalties are applied to the plant per severity tier.

Each tier must include:

* `quality`: Reduction in final yield quality
* `health`: Reduction in overall plant health

These values are cumulative and applied when the condition escalates.

***

## Cure Field

The `cure` field defines which item is required to remove the pest or disease. Structure:

```lua
cure = {
    id = "item_name",
    label = "Item Label",
    amount = 1
}
```

This item must exist in your inventory system.

***

## Increase Rate

This value determines how quickly the condition escalates to a higher severity level. \
Lower values escalate faster.

```lua
increase_rate = 4 -- escalates every 4 ticks
```

***

## Registering New Pests or Diseases

To add a new entry:

1. Open `data/pests.lua` or `data/diseases.lua`.
2. Add a new key to the returned table.
3. Define the structure following the format above.
4. Ensure `plant_types` includes only valid keys from `data/plants.lua`.

***

## Example: Adding a New Disease

```lua
powdery_mildew = {
    plant_types = { "tomato" },
    label = "Powdery Mildew",
    severity = "low",
    effects = {
        low = { quality = 2, health = 1 },
        medium = { quality = 4, health = 2 },
        high = { quality = 6, health = 3 }
    },
    cure = { id = "sodium_bicarbonate", label = "Sodium Bicarbonate", amount = 1 },
    increase_rate = 5
}
```

Add this to the return table in `data/diseases.lua`.\
Pests follow the same format.


# Adjusting Environment

The environment system defines how external conditions affect plant behaviour based on **altitude**, **weather**, and **ground type**. These modifiers are applied dynamically during plant lifecycle checks.

***

## Modifier Fields

Each modifier table can include the following fields:

| Field     | Description                                                  |
| --------- | ------------------------------------------------------------ |
| `growth`  | Growth rate multiplier. Higher = faster growth.              |
| `water`   | Water usage multiplier. Higher = faster water loss.          |
| `food`    | Food consumption multiplier. Higher = faster depletion.      |
| `pests`   | Pest chance multiplier. Higher = more frequent infestation.  |
| `disease` | Disease chance multiplier. Higher = more frequent infection. |

***

## Altitude Modifiers

Altitude affects plant efficiency based on their vertical Z position.

```lua
altitudes = {
    { min = 0, max = 100, growth = 1.2, water = 1.0, food = 1.0, pests = 1.0, disease = 1.0 },
    { min = 101, max = 500, growth = 1.0, water = 1.1, food = 1.1, pests = 1.1, disease = 1.1 },
    { min = 501, max = 1000, growth = 0.9, water = 1.2, food = 1.2, pests = 1.2, disease = 1.2 },
    { min = 1001, max = 1500, growth = 0.8, water = 1.3, food = 1.3, pests = 1.3, disease = 1.4 },
    { min = 1501, max = 2500, growth = 0.6, water = 1.5, food = 1.5, pests = 1.5, disease = 1.7 }
}
```

***

## Weather Modifiers

Each weather type defines multipliers applied during that weather state.

```lua
weather_modifiers = {
    CLEAR = { growth = 1.0, water = 1.0, food = 1.0, pests = 1.0, disease = 1.0 },
    EXTRASUNNY = { growth = 1.2, water = 1.8, food = 1.0, pests = 1.2, disease = 0.8 },
    CLOUDS = { growth = 1.0, water = 0.9, food = 1.0, pests = 1.0, disease = 1.1 },
    OVERCAST = { growth = 0.9, water = 0.8, food = 1.0, pests = 0.9, disease = 1.2 },
    RAIN = { growth = 0.9, water = 0.5, food = 1.0, pests = 0.8, disease = 1.3 },
    CLEARING = { growth = 1.0, water = 1.0, food = 1.0, pests = 1.0, disease = 1.0 },
    THUNDER = { growth = 0.8, water = 0.4, food = 1.1, pests = 0.7, disease = 1.5 },
    SMOG = { growth = 0.8, water = 1.2, food = 1.1, pests = 1.0, disease = 1.3 },
    FOGGY = { growth = 0.9, water = 0.7, food = 1.0, pests = 1.0, disease = 1.4 },
    XMAS = { growth = 0.7, water = 0.6, food = 1.0, pests = 0.5, disease = 1.2 },
    SNOW = { growth = 0.6, water = 0.5, food = 1.2, pests = 0.4, disease = 1.5 },
    SNOWLIGHT = { growth = 0.7, water = 0.6, food = 1.1, pests = 0.6, disease = 1.3 },
    BLIZZARD = { growth = 0.5, water = 0.3, food = 1.5, pests = 0.2, disease = 1.8 },
    HALLOWEEN = { growth = 1.0, water = 1.0, food = 1.0, pests = 1.5, disease = 1.2 },
    NEUTRAL = { growth = 1.0, water = 1.0, food = 1.0, pests = 1.0, disease = 1.0 }
}
```

***

## Ground Modifiers

Ground types are keyed by material hash and define how soil affects plant performance.

```lua
ground_modifiers = {
    [1288448767] = { name = "GRASS", growth = 1.2, water = 1.2, food = 1.0, pests = 1.2, disease = 1.1 },
    [1109728704] = { name = "MUD", growth = 1.0, water = 1.5, food = 1.0, pests = 1.0, disease = 1.3 },
    [223086562] = { name = "SOIL", growth = 1.3, water = 1.3, food = 1.0, pests = 1.0, disease = 1.0 },
    [2128369009] = { name = "SAND", growth = 0.7, water = 0.5, food = 1.2, pests = 0.8, disease = 0.9 },
    [2461440131] = { name = "CLAY", growth = 0.9, water = 1.4, food = 1.1, pests = 1.1, disease = 1.3 },
    [3594309083] = { name = "ROCK", growth = 0.6, water = 0.3, food = 1.2, pests = 0.9, disease = 0.8 },
    [1144315879] = { name = "SNOW", growth = 0.5, water = 0.4, food = 1.3, pests = 0.5, disease = 1.5 },
    [4170197704] = { name = "GRAVEL", growth = 0.8, water = 0.6, food = 1.1, pests = 1.0, disease = 1.0 },
    [3008270349] = { name = "CONCRETE", growth = 0.3, water = 0.2, food = 1.5, pests = 0.5, disease = 0.7 },
    [951832588] = { name = "ASPHALT", growth = 0.2, water = 0.1, food = 1.5, pests = 0.3, disease = 0.6 },
    [2409420175] = { name = "WOOD", growth = 0.5, water = 0.5, food = 1.0, pests = 0.8, disease = 0.9 },
    [1333033863] = { name = "LEAVES", growth = 1.1, water = 1.2, food = 1.0, pests = 1.3, disease = 1.2 },
    [2352068586] = { name = "MARSH", growth = 1.0, water = 1.8, food = 1.1, pests = 1.2, disease = 1.4 },
    [581794674] = { name = "TUNDRA", growth = 0.6, water = 0.7, food = 1.2, pests = 0.7, disease = 1.3 },
    [3833216577] = { name = "FARM_SOIL", growth = 1.5, water = 1.2, food = 1.0, pests = 1.0, disease = 1.0 }
}
```


# Installation

Getting setup with the Planting script is straight forward.\
Just follow the steps below and you'll be selling your nan's favourite cat in no time.

***

{% stepper %}
{% step %}

### Download

Once you have purchased your resource download it from **Keymaster**
{% endstep %}

{% step %}

### Adding The Resource

After downloading the resource extract and add it into your server resources.
{% endstep %}

{% step %}

### Ensure It

Add `ensure planting_system` to your `server.cfg`&#x20;
{% endstep %}

{% step %}

### Downloading Dependencies

Get the latest release versions of our dependency resources if you do not already have them:

* [**BOII Development Tool Kit**](https://github.com/boiidevelopment/bdtk/releases)
* [**BOII Development UI Kit**](https://github.com/boiidevelopment/bduk/releases)
  {% endstep %}

{% step %}

### Adding The Dependencies

Add the `bdtk` & `bduk` resource into your server, and ensure them.\
Your order should be like so:

```
ensure bduk
ensure bdtk # must be started before the script
ensure planting_system
```

{% endstep %}

{% step %}

### Resource Configuration

For resource configuration read the [**Guides**](/fivem-paid-resources/planting-system/guides)
{% endstep %}

{% step %}

### Restart Your Server

Restart your server and you will be good to go
{% endstep %}
{% endstepper %}


# Configuration

Script configuration boils down to a small set of `convars`, a config file and static data files.\
Convars overrides can be set in your `server.cfg` or you can leave them as default.&#x20;

For data configurations refer to the [**Guides**](/fivem-paid-resources/planting-system/guides) section.

***

## **Convar Settings**

You can override the default settings of these convars within your `server.cfg` for more about convars read: [**FiveM Docs - Convars**](https://docs.fivem.net/docs/scripting-reference/convars/).

```lua
planting.resource_name = GetCurrentResourceName()
planting.is_server = IsDuplicityVersion()
planting.version = GetResourceMetadata(planting.resource_name, "version", 0) or "unknown"
planting.debug_mode = GetConvar("planting:debug_mode", "false") == "true"
planting.language = GetConvar("planting:language", "en")
```

***

## Config

```lua
cfg.growers = {
    save_interval = 5
}

cfg.plants = {

    plant_range_check = 3.0,
    plant_limit = 5,

    critical_health_threshold = 20,
    critical_health_quality_decay = 5,
    quality_to_kill_plant = 0,
    
    save_interval = 10,

    altitude_penalty = 1.0,

    default_ground = 1288448767,
    default_weather = "CLEAR",

    pest_factor = 1.2,
    disease_factor = 1.3,
    factor_fallback = 1.0,

    base_water_loss = { min = 2, max = 5 },
    base_fert_loss = { min = 1, max = 3 },
    critical_water_threshold = 20,
    critical_fert_threshold = 10,

    base_decay_rate = { min = 1, max = 5 },

    growth_health_threshold = 21,
    growth_water_threshold = 21, 
    growth_fert_threshold = 11,

    base_growth_rate = { min = 1, max = 3 },
    
    pest_apply_chance = 0.02,
    disease_apply_chance = 0.02,

    default_water = 25.0,
    default_fert = 25.0,
    default_quality = 100.0
}
```


# Drying Racks

{% hint style="danger" %}

### BETA RELEASE

Resource is currently a BETA release.\
It will be moved away from this as soon as some feedback has been received.&#x20;
{% endhint %}

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

***

## Overview

**Tired of drying your crops on invisible shelves and pretending that’s immersive?**\
This isn’t another lazy loop with progress bars taped to it.

This is a fully modular, server-validated, prop-spawning, UI-powered **drying rack system** - that just works.

With **per-slot drying props**, **multi-strain support**, **real-time DUI updates**, and **config-driven logic**, this system gives your drug economy the visual feedback and control it deserves.\
From single strain to fully chaotic rack mixing - your server can finally stop faking the most satisfying part of processing.

Oh, and it’s synced. **Properly.**\
All players. All props. All progress.\
No weird delays. No imaginary drying.\
Just **real in-world results**, defined by you.

It’s probably the most over-engineered way to turn wet crops into dry ones.\
And your players? They'll love every second of it.

***

## Features

* Define unlimited strains — each with its own wet/dry items, images, and logic
* Live DUI for each rack with progress bars and contextual actions
* Placeable racks with ghost preview, full rotation, and server-confirmed placement
* Visual drying props per item added what you add is what you see
* Slot-based logic each strain gets its own rack slot
* Server-validated item flow with zero trust in the client
* Fully extensible via `plants.lua` and `racks.lua`
* Support for rack capacity limits, metadata timers, and strain-specific behavior

***

## Dependencies

* [**BOII Development Tool Kit**](https://github.com/boiidevelopment/bdtk/releases)
* [**BOII Development UI Kit**](https://github.com/boiidevelopment/bduk/releases)
* [**OxMySQL**](https://github.com/CommunityOx/oxmysql)

***

## **Quick Install**

1. Add `boii_drying_racks` to your server resources
2. Add `REQUIRED.sql` into your database
3. Add `ensure boii_drying_racks` to your `server.cfg` after any dependencies
4. Customise strains and racks to your liking in `custom/data` files
5. Add images from `images` folder into your inventory script
6. Add items into your core/inventory
7. Restart the server and enjoy&#x20;

***

## Support

For help, bugs, questions join the support Discord: [**https://discord.gg/MUckUyS5Kq**](https://discord.gg/MUckUyS5Kq)

We'll try to get back to you ASAP. Unless it's 3am. Then you're on your own.


# Guides

placeholder


# Fuel Siphon

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

***

## Overview

Fuel siphoning.\
You've seen it in movies, heard about it on the news, and probably even done it yourself once or twice.\
Now, you can do it in FiveM with the our Fuel Siphon Script - because why shouldn't stealing gas be fun?

This script lets your players siphon fuel from just about anything that holds it: barrels, tanks, rusting wrecks, or that weird contraption you thought was a good idea at the time. It's a mini heist with all the right ingredients: police alerts, aggressive guards, and the sweet satisfaction of siphoning fuel right from under the authorities noses.

Want to siphon fuel as a desperate survivor just trying to keep the lights on? Go ahead.\
Need fuel for your next big heist, but don't feel like paying for it? Perfect.\
Just want to stand there and watch as fuel drips from a hose, puddles up beneath you, and chaos unfolds around you? We've got you covered.

But don't get too comfortable.\
This isn't a peaceful gardening experience.\
Cops can be alerted, and aggressive guards can spawn to stop you.\
Because what's a good crime without the chance of a little confrontation, right?

Watch particles drip, puddles form, and feel the thrill of committing a crime without consequences - if you're lucky enough.\
Otherwise, enjoy the complete chaos of siphoning fuel, attaching ropes, and hearing that sweet hiss of gasoline filling your bucket.

It's like fuelling up your car, except you're not paying for it, there's no attendant, and no one's asking questions.\
What could possibly go wrong? Everything, if the cops show up or the guards get a little too aggressive.\
But hey, that's what makes it fun, right?

***

## Features

* Siphon Fuel: Grab fuel from anything with a tank, barrel, or anything else that can hold gas. If it holds fuel, you can siphon it. Simple, right?
* Particle Effects: Watch as fuel drips and puddles up, just like in the movies, without the explosions (unless you're not good at this... then explosions might happen).
* Buckets and Ropes: Because what's a siphon without a bucket and rope? Every criminal needs a good setup.
* Multiple Prop Support: Add as many props as you like. More props = more fun. More fuel = more chaos.
* Simple Animations: Watch your character perform the art of siphoning with some nice animations. Because nothing says "professional thief" like a good animation.

***

## Dependencies

* [**BOII Development Tool Kit**](https://github.com/boiidevelopment/bdtk/releases) - For multi-framework compatibility

***

## Quick Install

1. Add the `boii_siphon` resource into your server resources
2. Add `ensure boii_siphone` to your `server.cfg`
3. Ensure all dependencies are started **before** the script
4. Add items from `Inventory-Items.md` into your inventory/framework core
5. Add images provided into your inventory
6. Restart and enjoy

***

## Support

For help, bugs, questions join the support Discord: [**https://discord.gg/MUckUyS5Kq**](https://discord.gg/MUckUyS5Kq)

We'll try to get back to you ASAP. Unless it's 3am. Then you're on your own.


# Guides

This section provides all the information you need to configure, extend, and use the Fuel Siphon system. Whether you're adding new siphon-able props, adjusting explosion chances, or integrating the system into your server's economy, these guides cover everything by topic.

***

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Adding or Modifying Props</td><td><a href="/files/ah2KsCwFoDWg1psNjQqs">/files/ah2KsCwFoDWg1psNjQqs</a></td><td><a href="/pages/cPgqHaDXjQIwYqixYlfG">/pages/cPgqHaDXjQIwYqixYlfG</a></td></tr></tbody></table>


# Adding or Modifying Props

The **Fuel Siphon Script** comes with several default prop groups pre-configured for you. \
These default props allow players to siphon fuel from tanks, trailers, gas pumps, and more. \
However, the system is fully customizable, allowing you to add new siphon-able props and modify existing ones.

***

## **How to Add or Modify Props**

To add or modify siphon-able props, you'll need to edit the `data.props` module in the script. \
Here’s a breakdown of how each field works:

```lua
return {
    small_trailer = {
        props = { `prop_air_fueltrail1`, `prop_air_fueltrail2` }, -- List of props this applies to
        label = "Siphon Gasoline", -- Text shown in target + UI
        icon = "fas fa-gas-pump", -- FontAwesome icon for DUI + targeting
        duration = 12, -- Time (seconds) to fill the bucket
        cooldown = 120, -- Cooldown time (seconds) before siphoning again
        global_cooldown = true, -- If true, cooldown is shared by all players

        alerts = { -- Optional police alerts
            chance = 100, -- % chance to alert police
            jobs = { "police", "fib" }, -- Job names to alert
            on_duty_only = true -- Trigger only for on duty officers
        },

        guard = { -- Optional hostile NPCs
            chance = 0, -- % chance to spawn guards on siphon start
            count = { min = 3, max = 5 }, -- Random number of peds in range
            models = { `s_m_y_armymech_01` }, -- Ped model(s) to choose from
            weapons = { -- Weighted weapon list (higher = more likely)
                { hash = `WEAPON_WRENCH`, weight = 50 }, -- Common melee weapon
                { hash = `WEAPON_CARBINERIFLE`, weight = 50, ammo = 100 } -- Rare ranged weapon with ammo
            }
        },

        explode = { -- Optional explode chance
            chance = 0,  -- Chance of explosion happening
            explode_when = "random", -- "on_collect" - When player collects | "random" - Randomly throughout siphon process
            damage_radius = 10.0,  -- Radius of the explosion
            damage = 200.0,  -- Damage caused by explosion
            explosion_type = 2  -- Explosion type (2 is a normal explosion)
        },

        reward = { -- Reward given after successful siphon
            id = "gasoline", -- Internal item name
            label = "Gasoline", -- Friendly display name
            min = 2, max = 5 -- Random amount between min and max
        }
    }
}
```

***

## **Prop Options Explained**

1. **props**:
   * This is the list of prop models that the siphoning system will apply to. You can add or remove prop models here as needed.
2. **label**:
   * This is the label that will be displayed in the target UI when interacting with the prop.
3. **icon**:
   * FontAwesome icon used for UI representation and targeting. You can use any supported icon here.
4. **duration**:
   * The duration (in seconds) it takes to siphon fuel from the prop.
5. **cooldown**:
   * The cooldown (in seconds) before players can siphon the same prop again.
6. **global\_cooldown**:
   * If set to `true`, the cooldown is shared globally across all players. If set to `false`, each player will have their own cooldown.
7. **alerts**:
   * **chance**: The chance (in percentage) to alert the police when siphoning fuel.
   * **jobs**: Specifies which jobs (e.g., police, fib) will be alerted.
   * **on\_duty\_only**: If set to `true`, only on-duty officers will be alerted.
8. **guard**:
   * **chance**: The percentage chance for guards to spawn when siphoning fuel.
   * **count**: Defines the random number of guards that can spawn.
   * **models**: The list of NPC models that can spawn as guards.
   * **weapons**: Defines the weapons the guards can have, with weights to make certain weapons more likely.
9. **explode**:
   * **chance**: The percentage chance that an explosion will occur during siphoning.
   * **explode\_when**: Defines when the explosion happens (`"on_collect"` for when the siphon is collected, `"random"` for random explosions).
   * **damage\_radius**: The radius of the explosion.
   * **damage**: The damage caused by the explosion.
   * **explosion\_type**: The type of explosion (2 is a normal explosion).
10. **reward**:
    * **id**: The internal ID for the reward item.
    * **label**: The label for the reward item.
    * **min** and **max**: Defines the random range of the reward given after successful siphoning.

***

## **Adding New Props**

To add new props, simply copy one of the existing prop definitions and change the model names, labels, icons, and other values as needed. You can even modify the behaviour (e.g., change the explosion chance, duration, or reward).

For example, if you want to add a new prop for a fuel barrel, you would do something like:

```lua
new_fuel_barrel = {
    props = { `prop_fuel_barrel_01` }, -- New prop model
    label = "Siphon Fuel Barrel",
    icon = "fas fa-gas-pump",
    duration = 10,
    cooldown = 60,
    reward = { id = "gasoline", label = "Gasoline", min = 5, max = 10 }
}
```

You can add this entry to the `data.props` table, and it will become a siphonable object in your game!

***

## **Final Steps**

Once you’ve added or modified props, the new settings will be applied automatically. \
Restart the server or resource for the changes to take effect.


# Installation

{% stepper %}
{% step %}

### Add to Server Resources

Add the `boii_siphon` resource into your server resources
{% endstep %}

{% step %}

### Ensure It

Add `ensure boii_siphone` to your `server.cfg`
{% endstep %}

{% step %}

### Download Dependencies

This script requires our dev kit for multi framework compatibility make sure you have downloaded [**BOII Development Tool Kit**](https://github.com/boiidevelopment/bdtk/releases) and ensured it before the script
{% endstep %}

{% step %}

### Add Items

Add items from `Inventory-Items.md` into your inventory/framework core
{% endstep %}

{% step %}

### Add Images

Add images provided into your inventory
{% endstep %}

{% step %}

### Restart Server

Restart and enjoy you should be good to go
{% endstep %}
{% endstepper %}


# Configuration

Script configuration boils down to a small set of `convars` and static data files.\
Convars overrides can be set in your `server.cfg` or you can leave them as default.&#x20;

Defining new props can be done through static `data` files for more on this read: [**Adding or Modifying Props**](/fivem-paid-resources/fuel-siphon/guides/adding-or-modifying-props)**.**

You can create new locale files within `custom/locales` make sure you upgrade the language convar to your chosen language.

***

## **Convars**

* **`siphon:debug_mode`** – Set to `"true"` for debug mode, `"false"` to disable. Default: `"false"`.
* **`siphon:image_file_path`** – Path to the image files for UI. Default: `"nui://siphon/images/"`.
* **`siphon:language`** – Sets the language for notifications. Default: `"en"`. Change in `server.cfg` as needed.

***

## **Static Data Files**

You can define new siphon-able props (e.g., barrels, tanks) in the `custom.data.props` folder. \
This is where you can tweak siphoning behaviour, rewards, and durations.

More on this read: [**Adding or Modifying Props**](/fivem-paid-resources/fuel-siphon/guides/adding-or-modifying-props)**.**

***

## **Locales**

Create new language files in `custom.locales` for custom translations. \
Make sure to set the `siphon:language` convar in `server.cfg` to your desired language (e.g., `"fr"` for French).

```lua
return {
    --- @section Debug

    debug_rope_attached = "Rope attached for %s",
    started_siphon = "Started siphon scene for %s",
    collected_siphon = "Collected from %s",
    qb_trigger = "qb-target triggered for %s",
    ox_trigger = "ox_target triggered for %s",

    --- @section Notifications

    notify_header = "SIPHON",
    not_ready = "The process hasnt finished yet.. please wait...",
    bucket_ready = "The siphon process has finished you can collect it.",
    already_tapped = "This location has already been tapped.",
    collect_success = "You collected %dx %s.",
    no_siphon_kit = "What are you planning on siphoning with.. your mouth? Come back with the right tools.",

    --- @section Police Alerts
    
    alert_label = "10-33 | Suspicious Activity"
}
```


# Guide Books

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

***

Docs will be updated for this asap.

Its pretty straight forward as is.&#x20;


# boii\_utils

Developer Utility Library with Bridges and Common UI Elements

![](https://github.com/user-attachments/assets/e80578e0-42a6-499d-93fb-1eec1716f196)

## Overview

***

Welcome to `boii_utils` — your new favorite excuse to never write boilerplate code again.

This all-in-one, modular, feature-stuffed utility library is built specifically for FiveM script developers who are tired of reinventing the wheel every time they touch a new resource.

In v2.0, everything’s been rewritten from the ground up:

* The bloat? Gone.
* The bugs? Squashed.
* The logic? Rewired with duct tape and ambition.
* The license? MIT, because freedom tastes better without GPL breath.

What you’re getting now is a cleaner, faster, actually organized toolkit designed to make your life easier and your codebase prettier.\
Use just what you need, ignore the rest, and regain a little sanity.

## Why use `boii_utils`?

***

* **Simplifies Scripting:** Functions that just work, without needing a 12-tab Stack Overflow deep dive.
* **Prebuilt Systems:** Fully modular. Plug in, power up, and pretend you built it from scratch.
* **Reduces Dev Load:** Spend less time writing boilerplate and more time watching your players ignore your server rules.

## Highlights

***

#### **Framework & UI Bridges:** Making peace treaties between frameworks since inception.

* **Framework Bridge:** `boii`, `esx`, `nd`, `ox`, `qb`, `qbx` because commitment issues are real.
* **Notification Bridge:** `default`, `boii`, `esx`, `okok`, `ox`, `qb` every flavour of annoying popup you could desire.
* **DrawText Bridge:** `default`, `boii`, `esx`, `okok`, `ox`, `qb` for when you really need to make your players read something.

#### **Standalone Systems:** Because frameworks shouldn’t hold all the power.

* **Callbacks:** For when you're tired of yelling into the void; now it yells back, politely.
* **Commands:** Database-backed permissions included, or switch to Ace if you insist on complicating things.
* **Licences:** Comprehensive license system including theory tests, practical tests, points management, and revoking, like your local DMV, minus the soul-crushing lines.
* **XP:** Level-up system with growth factors and max levels, because who doesn't love arbitrary numbers going up?

#### **Unique Scripting Modules:** Pre-made shortcuts for the lazy genius in all of us.

* **Characters Module:** Enough functions to build character creators, clothing stores, tattoos, everything you need to keep your players staring at themselves for hours.
* **Vehicles Module:** If it drives and you can customize it, these functions probably have you covered.

#### **UI Elements:** The obligatory flashy bits to trick players into thinking you know what you're doing.

* **Action Menu:** Bored of spinning circles deciding what to click next? Try out a different take.
* **Context Menu:** Simple and effective, with header images.
* **Dialogue:** NPC conversations without the awkward silence.
* **DrawText:** Display on screen text clearly, ensuring players ignore it even faster.
* **Notify:** Notification styles for all occasions: `success`, `error`, `info`, `warning`, `primary`, `secondary`, `light`, `dark`, `critical`, `neutral`.
* **Progress Bar:** The classic, comforting sight of incremental loading bars.
* **Progress Circle:** Revolutionary innovation.. it's like a progress bar, but circular!

## All Modules

***

* **Framework Bridge:** Bridges multiple cores through one api.
* **Notifications Bridge:** Bridges multiple different notification resources through one api.
* **DrawText UI Bridge:** Bridges multiple different drawtext ui resources through one api.
* **Callbacks:** A standalone alternative to framework systems.
* **Characters:** Covers all character customisation relevant function with shared styles data.
* **Commands:** A standalone alternative to framework systems.
* **Debugging:** A couple of useful debugging functions.
* **Entities:** Everything related to entities (npc, vehicles, objects) within the game world.
* **Environment:** Set of function to cover everything enviroment, from current times to simulated seasons.
* **Geometry:** Suite of functions to simplfy geometric calculations in 2d and 3d space.
* **Items:** A standalone usable items registry to provide an alternative to framework specific systems.
* **Keys:** Includes a full static key list and simple function to get and retrieve keys by name or value.
* **Licences:** Full standalone licence system with support for point systems, theory and practical test markers, with support for licence revoking.
* **Maths:** Extends base `math.` functionality with a large suite of additional functions.
* **Methods:** Provides a system to register, remove, and trigger custom method callbacks on both the client and server.
* **Player:** Small amount of player related functions such as retrieving the players cardinal direction or running animations on the player with full attached prop support.
* **Requests:** Set of wrapper functions around cfx `Request` functions.
* **Strings:** Extends base `string.` functionality by adding some addition functions.
* **Tables:** Extends base `table.` functionality by adding some useful functions otherwise not already provided.
* **Timestamps:** Covers everything related to server side timestamps with formatted responses.
* **Vehicles:** Large suite of vehicle related functions, should include everything needed to create a vehicle customs resource.
* **Version:** Provides resource version checking from an externally hosted `.json` file.
* **XP:** Full standalone XP system with support for types, growth factors and max levels.

## UI Screenshots

***

![Menus and DrawText](https://i.ibb.co/PG7vKfPB/image-2025-03-15-004251413.png)\
![Progress Bars](https://i.ibb.co/9HkYnYqh/image-2025-03-15-004440049.png)

## Installation

***

You know the drill.

1. Drop it in your `resources/`.
2. Insert the included `REQUIRED.sql` into your database.
3. `ensure boii_utils`.
4. Restart your server.

Full setup guide: [`docs/2-Installation.md`](/old-docs/boii_utils/installation).

## Dependencies

***

* [**OxMySQL**](https://github.com/overextended/oxmysql/releases)

## License

***

Released under the **MIT License**.\
That means it’s free, open-source, and yours to use, modify, or build on — just don’t remove the license or the credit.

You’re welcome to profit from it, but let’s keep things respectful: don’t act like you wrote it all yourself. :heart:

## Contributing

***

Got code? Great. Got opinions? Even better.

If you’ve written something useful, spotted something broken, or just want eternal internet glory *(or shame)*, submit a pr.\
We welcome contributions, improvements, fixes, and clever feature requests.

You can also reach out through Discord if you would prefer.

## Support

***

Need help? Found a bug? Need to vent about a bug that isn’t from this library?\
Support is available through the BOII Development [**Discord**](https://discord.gg/MUckUyS5Kq).

> Support Hours: Mon–Fri, 10AM–10PM GMT

Outside those hours? Pray to the debug gods or leave a message.

## Links

***

* [**Discord**](https://discord.gg/MUckUyS5Kq)
* [**Documentation**](https://docs.boii.dev/) *(your already here* :wink:*)*
* [**GitHub**](https://github.com/boiidevelopment)
* [**Tebex Legacy QB Resources**](https://boii.tebex.io)
* [**Tebex New Multiframework Resources**](https://boiidevelopment.tebex.io/)
* [**YouTube**](https://youtube.com/boiidevelopment)


# Installation

Before installing the library into your server please make sure you have the following dependencies in your server resources, setup and ready to go.

## Dependencies

***

{% hint style="warning" %}
If you want to make use of the framework or ui bridges you of course need the correct framework core/resources
{% endhint %}

* [**OxMySQL**](https://github.com/overextended/oxmysql/releases)

## Downloading The Library

***

* [**boii\_utils**](https://github.com/boiidevelopment/boii_utils/releases)

## Installation

***

The library is mostly drag and drop unless you want to modify some functionality.\
For example; by default `AUTO_DETECT_FRAMEWORK` is enabled, if you want to run the library standalone you need to modify the `ENV` values for this.

You can find more in-depth details on configuring the library in **3-Configuration.md**.\
If you want to make configuration changes do these before adding the library, save on adding twice.

### Database Tables

Included with the library are some `.sql` files which you need to add into your database.

* `REQUIRED.sql`: This is is the main tables used by the library for user accounts and bans.
* `frameworks/*.sql`: These are tables to cover `utils_xp` and `utils_licences` for the libraries standalone systems.

The libraries standalone command system uses the `utils_users` table from `REQUIRED.sql` to handle admin permissions.\
Once you have joined the server for the first time you can update this in your database.

Default Ranks: `("member", "mod", "admin", "dev", "owner")`

### Adding The Library

1. Add the `boii_utils` into your server resources.
2. Add `ensure boii_utils` into your `server.cfg`;

* You can add the libraries convars also if you would like; view the included `convars.cfg` file, or they are covered in more detail in **3-Configuration.md**.

3. If all installation steps have been completed *(and optional configuration customisation)*, restart your server and you should be up and running.


# Configuration

Configuration for the library is primary handled through convars.\
If you are unsure on how convars work view the CFX documentation [**HERE**](https://docs.fivem.net/docs/scripting-reference/convars/)

Some configuration options do have manual overrides, you can find these under `ENV` in `init.lua`

## Environment Variables

***

```lua
ENV = setmetatable({
    --- @section Cache

    DATA = {},
    MODULES = {},

    --- @section Natives

    RESOURCE_NAME = GetCurrentResourceName(),
    IS_SERVER = IsDuplicityVersion(),

    --- @section User Registry

    DEFFERAL_UPDATE_MESSAGES = GetConvar("utils:deferals_updates", "true") == "true", -- Defferal connection messages, disable with convars.
    UNIQUE_ID_PREFIX = GetConvar("utils:unique_id_prefix", "USER_"), -- Prefix is combined with digits below to create a unique id e.g, "USER_12345"
    UNIQUE_ID_CHARS = GetConvar("utils:unique_id_chars", "5"), -- Amount of random characters to use after prefix e.g, "ABC12"

    --- @section Framework Bridge

    --- Supported Frameworks: If you have changed the name of your core resource folder update it here.
    FRAMEWORKS = {
        -- If you use multiple cores you can adjust the priority loading order by changed the arrangement here.
        { key = "boii_core", resource = "boii_core" },
        { key = "esx", resource = "es_extended" },
        { key = "nd", resource = "ND_Core" },
        { key = "ox", resource = "ox_core" },
        { key = "qb", resource = "qb-core" },
        { key = "qbx", resource = "qbx_core" },
    },
    AUTO_DETECT_FRAMEWORK = true, -- If true FRAMEWORK convar setting will be overwritten with auto detection.
    FRAMEWORK = GetConvar("utils:framework", "standalone"), -- This should not be changed, set up convars correctly and change there if needed.

    --- @section UI Bridges

    --- Supported DrawText UIs: If you have changed the name of a resource folder update it here.
    DRAWTEXTS = {
        -- If you use multiple drawtext resource you can adjust the priority loading order by changed the arrangement here.
        { key = "boii", resource = "boii_ui" },
        { key = "esx", resource = "es_extended" },
        { key = "okok", resource = "okokTextUi" },
        { key = "ox", resource = "ox_lib" },
        { key = "qb", resource = "qb-core" }
    },
    AUTO_DETECT_DRAWTEXT = true, -- If true DRAWTEXT convar setting will be overwritten with auto detection.
    DRAWTEXT = GetConvar("utils:drawtext_ui", "default"), -- This should not be changed, set up convars correctly and change there if needed.

    --- Supported Notifys: If you have changed the name of a resource folder update it here.
    NOTIFICATIONS = {
        -- If you use multiple notify resources you can adjust the priority loading order by changed the arrangement here.
        { key = "boii", resource = "boii_ui" },
        { key = "esx", resource = "es_extended" },
        { key = "okok", resource = "okokNotify" },
        { key = "ox", resource = "ox_lib" },
        { key = "qb", resource = "qb-core" }
    },
    AUTO_DETECT_NOTIFY = true, -- If true NOTIFY convar setting will be overwritten with auto detection.
    NOTIFY = GetConvar("utils:notify", "default"), -- This should not be changed, set up convars correctly and change there if needed.

    --- @section Timers

    CLEAR_EXPIRED_COOLDOWNS = 5 -- Timer to clear expired cooldowns from cache in mins; default 5mins.

}, { __index = _G })
```

## Convars

***

You can add the following convars into your `server.cfg` to have further control over the library.\
Since many people seem to misunderstand how to configure these, default values are included to prevent misconfiguration.

### Manual Framework Control

You can manually specify the framework with the following convar:

```bash
setr utils:framework "standalone" # Replace "standalone" with "boii_core", "esx", "nd", "ox", "qb", or "qbx".
```

* If set, this will override auto-detection (`AUTO_DETECT_FRAMEWORK`).
* If left unset, the system will attempt to auto-detect the framework.

### Deferals Update Messages

Control whether deferal messages (e.g., connection checks, bans, etc.) are updated during login.

```bash
setr utils:deferals_updates "true" # Set to "false" to disable.
```

* If true, deferal messages update dynamically during login.
* If false, only the initial message will display.

### Unique ID Generation

Control how unique player IDs are generated.

```bash
setr utils:unique_id_prefix "USER_" # Prefix for generated unique IDs.
setr utils:unique_id_chars "5" # Number of random characters in the ID.
```

* Example Output: `USER_ABC12`
* `unique_id_prefix`: Set a custom prefix for player IDs.
* `unique_id_chars`: Defines the number of random letters/numbers after the prefix.

### DrawText UI Selection

Choose which DrawText UI the library should use.

```bash
setr utils:drawtext_ui "default" # Options: "default", "boii", "esx", "okok", "ox", "qb".
```

* If unset, defaults to `"default"`.

### Notification System Selection

Choose which notification system to use.

```bash
setr utils:notify "default" # Options: "default", "boii", "esx", "okok", "ox", "qb".
```

* If unset, defaults to `"default"`.

### Cooldown Cleanup Timer

Set how often expired cooldowns are removed from the cache.

```bash
setr utils:clear_expired_cooldowns "5" # Default: 5 minutes.
```


# Modules

The library is divided into useful `modules` you can import into your resource through the export `exports.boii_utils:get(...)`.

## Available Modules

***

Below is a quick list of all current available modules and a brief description of what the module can do.\
For more detailed instructions on whats available in each module view **5-API-Reference.md**

### Bridges

Bridges allow seamless integration with multiple resources through a single API, reducing the need for writing framework-specific code.\
They detect available resources and route accordingly, making scripts more flexible and future-proof.

* **Framework Bridge:** Supported by default; `"boii_core", "esx", "nd", "ox", "qb", "qbx"`.
* **DrawText UI Bridge:** Supported by default; `"default", "boii", "esx", "okok", "ox", "qb"`.
* **Notifications Bridge:** Supported by default; `"default", "boii", "esx", "okok", "ox", "qb"`.

### Standalone Systems

These systems are designed to replace framework-locked features, allowing resources to function independently of any specific core.\
They also provide a solution for supporting servers that lack certain mechanics.

* **Callbacks:** Unified callback system to replace framework-specific handlers.
* **Commands:** Database-based command system with configurable permissions.
* **Item Registry:** Standalone item system for managing usable items.
* **Licence System:** Full licence handling, including theory/practical tests, points, and revocation.
* **Player XP:** Levelling and experience system with growth factors and max levels.

### Other Modules

* **Characters:** A unique module covering everything related to character creation/customisation, including shared styles data.
* **Cooldowns:** Allows for setting player-specific, resource-based, or global cooldowns throughout the game world.
* **Debugging:** A set of useful debugging functions to aid development and troubleshooting.
* **Entities:** Covers everything related to entities (NPCs, vehicles, objects) within the game world.
* **Environment:** Functions to handle environmental elements such as time, weather, and simulated seasons.
* **Geometry:** A suite of functions to simplify geometric calculations in both 2D and 3D space.
* **Keys:** Provides a full static key list and functions for retrieving keys by name or value.
* **Maths:** Extends base `math.` functionality with additional useful mathematical functions.
* **Player:** Includes various player-related functions such as retrieving the player's cardinal direction or playing animations with prop support.
* **Requests:** Wrapper functions around CFX `Request` functions to simplify resource requests.
* **Strings:** Extends base `string.` functionality by adding additional helper functions.
* **Tables:** Enhances base `table.` functionality by providing additional utility functions.
* **Timestamps:** Handles everything related to server-side timestamps with formatted responses.
* **Vehicles:** A comprehensive suite of vehicle-related functions, covering all aspects needed for a vehicle customization system.
* **Version:** Provides resource version checking from an externally hosted `.json` file.

## Importing Modules

***

To keep things more simple for end users all module importing is handled through exports.\
For example:

```lua
local CALLBACKS <const> = exports.boii_utils:get("modules.callbacks") -- Gets callbacks module
```

## Using Modules

***

Once you have imported a module it is ready to be used.\
Below is a quick example of using the `callbacks` module.

### Server

```lua
local CALLBACKS <const> = exports.boii_utils:get("modules.callbacks")

CALLBACKS.register("some_event_name", function(source, data, cb)
    if source == 0 then 
        cb(false, "Callback must be triggered by a player!")
        return
    end
    cb(true, "Callback successful!")
end)
```

### Client

```lua
local CALLBACKS <const> = exports.boii_utils:get("modules.callbacks")

CALLBACKS.trigger("some_event_name", nil, function(success, message)
    local success_text = success and "Success!" or "Failed!"
    print(("Callback %s Message: %s"):format(success_text, message)) 
    -- Output: "Callback Success! Message: Callback successful!" or "Callback Failed! Message: Callback must be triggered by a player!
end)
```

## Available Modules

***

```lua
exports.boii_utils:get("modules.core") -- Framework Bridge
exports.boii_utils:get("modules.notifications") -- Notifications Bridge
exports.boii_utils:get("modules.drawtext") -- DrawText UI Bridge
exports.boii_utils:get("modules.callbacks") -- Callbacks System
exports.boii_utils:get("modules.characters") -- Character Customisation
exports.boii_utils:get("modules.commands") -- Commands System
exports.boii_utils:get("modules.debugging") -- Debugging Utilities
exports.boii_utils:get("modules.entities") -- Entity Management
exports.boii_utils:get("modules.environment") -- Environment Functions
exports.boii_utils:get("modules.geometry") -- Geometry Calculations
exports.boii_utils:get("modules.items") -- Item Registry
exports.boii_utils:get("modules.keys") -- Key Management
exports.boii_utils:get("modules.licences") -- Licence System
exports.boii_utils:get("modules.maths") -- Extended Maths Functions
exports.boii_utils:get("modules.methods") -- Attaching and triggering custom logic on events
exports.boii_utils:get("modules.player") -- Player Utilities
exports.boii_utils:get("modules.requests") -- Request Handlers
exports.boii_utils:get("modules.strings") -- Extended String Functions
exports.boii_utils:get("modules.tables") -- Extended Table Functions
exports.boii_utils:get("modules.timestamps") -- Timestamp Utilities
exports.boii_utils:get("modules.vehicles") -- Vehicle Management
exports.boii_utils:get("modules.version") -- Version Checking
exports.boii_utils:get("modules.xp") -- XP System
```


# API

Below is a quick outline of the functions available in each module.\
Modules can be accessed in two ways:

#### Requiring the module:

```lua
local CORE <const> = exports.boii_utils:get("modules.core")

CORE.get_players()
```

#### Exports:

```lua
exports.boii_utils:get_players()
```

For more detailed API instructions each module has its own API file, this is simply a quick reference.

## Framework Bridge

***

### Server

```lua
    --- @section Function Assignments

    core.get_players = get_players
    core.get_player = get_player
    core.get_id_params = get_id_params
    core.get_insert_params = get_insert_params
    core.get_player_id = get_player_id
    core.get_identity = get_identity
    core.get_inventory = get_inventory
    core.get_item = get_item
    core.has_item = has_item
    core.add_item = add_item
    core.remove_item = remove_item
    core.update_item_data = update_item_data
    core.get_balances = get_balances
    core.get_balance_by_type = get_balance_by_type
    core.add_balance = add_balance
    core.remove_balance = remove_balance
    core.get_player_jobs = get_player_jobs
    core.player_has_job = player_has_job
    core.get_player_job_grade = get_player_job_grade
    core.count_players_by_job = count_players_by_job
    core.get_player_job_name = get_player_job_name
    core.adjust_statuses = adjust_statuses
    core.register_item = register_item

    --- @section Exports

    exports("get_players", get_players)
    exports("get_player", get_player)
    exports("get_id_params", get_id_params)
    exports("get_insert_params", get_insert_params)
    exports("get_player_id", get_player_id)
    exports("get_identity", get_identity)
    exports("get_inventory", get_inventory)
    exports("get_item", get_item)
    exports("has_item", has_item)
    exports("add_item", add_item)
    exports("remove_item", remove_item)
    exports("update_item_data", update_item_data)
    exports("get_balances", get_balances)
    exports("get_balance_by_type", get_balance_by_type)
    exports("add_balance", add_balance)
    exports("remove_balance", remove_balance)
    exports("get_player_jobs", get_player_jobs)
    exports("player_has_job", player_has_job)
    exports("get_player_job_grade", get_player_job_grade)
    exports("count_players_by_job", count_players_by_job)
    exports("get_player_job_name", get_player_job_name)
    exports("adjust_statuses", adjust_statuses)
    exports("fw_register_item", register_item) -- Registered as fw_register_item so does not conflict with internal item system.
```

### Client

```lua
    --- @section Function Assignments
    
    core.get_data = get_data
    core.get_identity = get_identity
    core.get_player_id = get_player_id
    
    --- @section Exports

    exports("get_data", get_data)
    exports("get_identity", get_identity)
    exports("get_player_id", get_player_id)
```

## DrawText UI Bridge

***

### Server

```lua
    --- @section Function Assignments

    drawtext.show = show_drawtext
    drawtext.hide = hide_drawtext

    --- @section Exports

    exports("drawtext_show", show_drawtext)
    exports("drawtext_hide", hide_drawtext)
```

### Client

```lua
    --- @section Function Assignments

    drawtext.show = show_drawtext
    drawtext.hide = hide_drawtext

    --- @section Exports

    exports("drawtext_show", show_drawtext)
    exports("drawtext_hide", hide_drawtext)
```

## Notifications Bridge

***

### Server

```lua
    --- @section Function Assignments

    notifications.send = notify

    --- @section Exports

    exports("send_notification", notify)
```

### Client

```lua
    --- @section Function Assignments

    notifications.send = notify

    --- @section Exports

    exports("send_notification", notify)
```

## Callbacks

***

### Server

```lua
    --- @section Function Assignments

    callbacks.register = register_callback

    --- @section Exports

    exports("register_callback", register_callback)
```

### Client

```lua
    --- @section Function Assignments

    callbacks.trigger = callback

    --- @section Exports

    exports("trigger_callback", callback)
```

## Characters

***

### Client

```lua
    --- @section Functions Assignments

    characters.get_style = get_style
    characters.reset_styles = reset_styles
    characters.get_clothing_and_prop_values = get_clothing_and_prop_values
    characters.set_ped_appearance = set_ped_appearance
    characters.update_ped_data = update_ped_data
    characters.change_player_ped = change_player_ped
    characters.rotate_ped = rotate_ped
    characters.load_character_model = load_character_model

    --- @section Exports

    exports("get_style", get_style)
    exports("reset_styles", reset_styles)
    exports("get_clothing_and_prop_values", get_clothing_and_prop_values)
    exports("set_ped_appearance", set_ped_appearance)
    exports("update_ped_data", update_ped_data)
    exports("change_player_ped", change_player_ped)
    exports("rotate_ped", rotate_ped)
    exports("load_character_model", load_character_model)
```

## Commands

***

### Server

```lua
    --- @section Function Assignments

    commands.register = register_command

    --- @section Exports

    exports("register_command", register_command)
```

### Client

```lua
    --- @section Function Assignments

    commands.get_chat_suggestions = get_chat_suggestions

    --- @section Exports

    exports("get_chat_suggestions", get_chat_suggestions)
```

## Cooldowns

***

### Server

```lua
    --- @section Function Assignments
    
    cooldowns.add = add_cooldown
    cooldowns.check = check_cooldown
    cooldowns.clear = clear_cooldown
    cooldowns.clear_expired_cooldowns = clear_expired_cooldowns
    cooldowns.clear_resource_cooldowns = clear_resource_cooldowns

    --- @section Exports

    exports("add_cooldown", add_cooldown)
    exports("check_cooldown", check_cooldown)
    exports("clear_cooldown", clear_cooldown)
    exports("clear_expired_cooldowns", clear_expired_cooldowns)
    exports("clear_resource_cooldowns", clear_resource_cooldowns)
```

## Debugging

***

### Shared

```lua
    --- @section Function Assignments

    debugging.print = debug_print

    --- @section Exports

    exports("debug_print", debug_print)
```

## Entities

***

### Client

```lua
    --- @section Function Assignments
    
    entities.get_nearby_objects = get_nearby_objects
    entities.get_nearby_peds = get_nearby_peds
    entities.get_nearby_players = get_nearby_players
    entities.get_nearby_vehicles = get_nearby_vehicles
    entities.get_closest_object = get_closest_object
    entities.get_closest_ped = get_closest_ped
    entities.get_closest_player = get_closest_player
    entities.get_closest_vehicle = get_closest_vehicle
    entities.get_entities_in_front_of_player = get_entities_in_front_of_player
    entities.get_target_ped = get_target_ped

    --- @section Exports

    exports("get_nearby_objects", get_nearby_objects)
    exports("get_nearby_peds", get_nearby_peds)
    exports("get_nearby_players", get_nearby_players)
    exports("get_nearby_vehicles", get_nearby_vehicles)
    exports("get_closest_object", get_closest_object)
    exports("get_closest_ped", get_closest_ped)
    exports("get_closest_player", get_closest_player)
    exports("get_closest_vehicle", get_closest_vehicle)
    exports("get_entities_in_front_of_player", get_entities_in_front_of_player)
    exports("get_target_ped", get_target_ped)
```

## Environment

***

### Client

```lua
    --- @section Function Assignments
    
    environment.get_weather_name = get_weather_name
    environment.get_game_time = get_game_time
    environment.get_game_date = get_game_date
    environment.get_sunrise_sunset_times = get_sunrise_sunset_times
    environment.is_daytime = is_daytime
    environment.get_current_season = get_current_season
    environment.get_distance_to_water = get_distance_to_water
    environment.get_zone_scumminess = get_zone_scumminess
    environment.get_ground_material = get_ground_material
    environment.get_wind_direction = get_wind_direction
    environment.get_altitude = get_altitude
    environment.get_environment_details = get_environment_details

    --- @section Exports

    exports("get_weather_name", get_weather_name)
    exports("get_game_time", get_game_time)
    exports("get_game_date", get_game_date)
    exports("get_sunrise_sunset_times", get_sunrise_sunset_times)
    exports("is_daytime", is_daytime)
    exports("get_current_season", get_current_season)
    exports("get_distance_to_water", get_distance_to_water)
    exports("get_zone_scumminess", get_zone_scumminess)
    exports("get_ground_material", get_ground_material)
    exports("get_wind_direction", get_wind_direction)
    exports("get_altitude", get_altitude)
    exports("get_environment_details", get_environment_details)
```

## Geometry

***

### Shared

```lua
    --- @section Function Assignments

    geometry.distance_2d = distance_2d
    geometry.distance_3d = distance_3d
    geometry.midpoint = midpoint
    geometry.is_point_in_rect = is_point_in_rect
    geometry.is_point_in_box = is_point_in_box
    geometry.is_point_on_line_segment = is_point_on_line_segment
    geometry.project_point_on_line = project_point_on_line
    geometry.calculate_slope = calculate_slope
    geometry.angle_between_points = angle_between_points
    geometry.angle_between_3_points = angle_between_3_points
    geometry.do_circles_intersect = do_circles_intersect
    geometry.is_point_in_circle = is_point_in_circle
    geometry.do_lines_intersect = do_lines_intersect
    geometry.line_intersects_circle = line_intersects_circle
    geometry.does_rect_intersect_line = does_rect_intersect_line
    geometry.closest_point_on_line_segment = closest_point_on_line_segment
    geometry.triangle_area_3d = triangle_area_3d
    geometry.is_point_in_sphere = is_point_in_sphere
    geometry.do_spheres_intersect = do_spheres_intersect
    geometry.is_point_in_convex_polygon = is_point_in_convex_polygon
    geometry.rotate_point_around_point_2d = rotate_point_around_point_2d
    geometry.distance_point_to_plane = distance_point_to_plane
    geometry.rotation_to_direction = rotation_to_direction
    geometry.rotate_box = rotate_box
    geometry.calculate_rotation_matrix = calculate_rotation_matrix
    geometry.translate_point_to_local_space = translate_point_to_local_space
    geometry.is_point_in_oriented_box = is_point_in_oriented_box

    --- @section Exports

    exports("distance_2d", distance_2d)
    exports("distance_3d", distance_3d)
    exports("midpoint", midpoint)
    exports("is_point_in_rect", is_point_in_rect)
    exports("is_point_in_box", is_point_in_box)
    exports("is_point_on_line_segment", is_point_on_line_segment)
    exports("project_point_on_line", project_point_on_line)
    exports("calculate_slope", calculate_slope)
    exports("angle_between_points", angle_between_points)
    exports("angle_between_3_points", angle_between_3_points)
    exports("do_circles_intersect", do_circles_intersect)
    exports("is_point_in_circle", is_point_in_circle)
    exports("do_lines_intersect", do_lines_intersect)
    exports("line_intersects_circle", line_intersects_circle)
    exports("does_rect_intersect_line", does_rect_intersect_line)
    exports("closest_point_on_line_segment", closest_point_on_line_segment)
    exports("triangle_area_3d", triangle_area_3d)
    exports("is_point_in_sphere", is_point_in_sphere)
    exports("do_spheres_intersect", do_spheres_intersect)
    exports("is_point_in_convex_polygon", is_point_in_convex_polygon)
    exports("rotate_point_around_point_2d", rotate_point_around_point_2d)
    exports("distance_point_to_plane", distance_point_to_plane)
    exports("rotation_to_direction", rotation_to_direction)
    exports("rotate_box", rotate_box)
    exports("calculate_rotation_matrix", calculate_rotation_matrix)
    exports("translate_point_to_local_space", translate_point_to_local_space)
    exports("is_point_in_oriented_box", is_point_in_oriented_box)
```

## Items

***

### Server

```lua
    --- @section Function Assignments

    items.register = register
    items.use = use_item

    --- @section Exports

    exports("register_item", register)
    exports("use_item", use_item)
```

## Keys

***

### Shared

```lua
    --- @section Function Assignments

    keys.get_keys = get_keys
    keys.get_key = get_key
    keys.get_key_name = get_key_name
    keys.print_key_list = print_key_list
    keys.key_exists = key_exists

    --- @section Exports

    exports("get_keys", get_keys)
    exports("get_key", get_key)
    exports("get_key_name", get_key_name)
    exports("print_key_list", print_key_list)
    exports("key_exists", key_exists)
```

## Licences

***

### Server

```lua
    --- @section Function Assignments

    licences.get_all = get_licences
    licences.get = get_licence
    licences.add = add_licence
    licences.remove = remove_licence
    licences.add_points = add_points
    licences.remove_points = remove_points
    licences.update = update_licence

    --- @section Exports

    exports('get_licences', get_licences)
    exports('add_licence', add_licence)
    exports('remove_licence', remove_licence)
    exports('add_points', add_points)
    exports('remove_points', remove_points)
    exports('update_licence', update_licence)
```

### Client

```lua
    --- @section Function Assignments

    licences.get_all = get_licences

    --- @section Exports

    exports('get_licences', get_licences)
```

## Maths

***

### Shared

```lua
    --- @section Function Assignment

    maths.round = round
    maths.calculate_distance = calculate_distance
    maths.clamp = clamp
    maths.lerp = lerp
    maths.factorial = factorial
    maths.deg_to_rad = deg_to_rad
    maths.rad_to_deg = rad_to_deg
    maths.circle_circumference = circle_circumference
    maths.circle_area = circle_area
    maths.triangle_area = triangle_area
    maths.mean = mean
    maths.median = median
    maths.mode = mode
    maths.standard_deviation = standard_deviation
    maths.linear_regression = linear_regression

    --- @section Exports

    exports('round', round)
    exports('calculate_distance', calculate_distance)
    exports('clamp', clamp)
    exports('lerp', lerp)
    exports('factorial', factorial)
    exports('deg_to_rad', deg_to_rad)
    exports('rad_to_deg', rad_to_deg)
    exports('circle_circumference', circle_circumference)
    exports('circle_area', circle_area)
    exports('triangle_area', triangle_area)
    exports('mean', mean)
    exports('median', median)
    exports('mode', mode)
    exports('standard_deviation', standard_deviation)
    exports('linear_regression', linear_regression)
```

## Player

***

### Shared

```lua
    --- @section Function Assignments

    player.get_distance_to_entity = get_distance_to_entity
    player.get_cardinal_direction = get_cardinal_direction

    --- @section Exports

    exports('get_distance_to_entity', get_distance_to_entity)
    exports('get_cardinal_direction', get_cardinal_direction)
```

### Client

```lua
    --- @section Function Assignments

    player.get_street_name = get_street_name
    player.get_region = get_region
    player.get_player_details = get_player_details
    player.get_target_entity = get_target_entity
    player.play_animation = play_animation

    --- @section Exports

    exports('get_street_name', get_street_name)
    exports('get_region', get_region)
    exports('get_player_details', get_player_details)
    exports('get_target_entity', get_target_entity)
    exports('play_animation', play_animation)
```

## Requests

***

### Client

```lua
    --- @section Function Assignments

    requests.model = request_model
    requests.interior = request_interior
    requests.texture = request_texture
    requests.collision = request_collision
    requests.anim = request_anim
    requests.anim_set = request_anim_set
    requests.clip_set = request_clip_set
    requests.audio_bank = request_audio_bank
    requests.scaleform_movie = request_scaleform_movie
    requests.cutscene = request_cutscene
    requests.ipl = request_ipl

    --- @section Exports

    exports('request_model', request_model)
    exports('request_interior', request_interior)
    exports('request_texture', request_texture)
    exports('request_collision', request_collision)
    exports('request_anim', request_anim)
    exports('request_anim_set', request_anim_set)
    exports('request_clip_set', request_clip_set)
    exports('request_audio_bank', request_audio_bank)
    exports('request_scaleform_movie', request_scaleform_movie)
    exports('request_cutscene', request_cutscene)
    exports('request_ipl', request_ipl)
```

## Strings

***

### Shared

```lua
    --- @section Function Assignments

    strings.capitalize = capitalize
    strings.random_string = random_string
    strings.split = split
    strings.trim = trim

    --- @section Exports

    exports('capitalize', capitalize)
    exports('random_string', random_string)
    exports('split', split)
    exports('trim', trim)
```

## Tables

***

### Shared

```lua
    --- @section Function Assignments

    tables.print = print_table
    tables.contains = table_contains
    tables.deep_copy = deep_copy
    tables.compare = deep_compare

    --- @section Exports

    exports('print_table', print_table)
    exports('table_contains', table_contains)
    exports('deep_copy', deep_copy)
    exports('deep_compare', deep_compare)
```

## Timestamps

***

### Server

```lua
    --- @section Function Assignments

    timestamps.get_timestamp = get_timestamp
    timestamps.convert_timestamp = convert_timestamp
    timestamps.get_current_date_time = get_current_date_time
    timestamps.add_days_to_date = add_days_to_date
    timestamps.date_difference = date_difference
    timestamps.convert_timestamp_ms = convert_timestamp_ms

    --- @section Exports

    exports("get_timestamp", get_timestamp)
    exports("convert_timestamp", convert_timestamp)
    exports("get_current_date_time", get_current_date_time)
    exports("add_days_to_date", add_days_to_date)
    exports("date_difference", date_difference)
    exports("convert_timestamp_ms", convert_timestamp_ms)
```

## Vehicles

***

### Client

```lua
    --- @section Function Assignments

    vehicles.get_vehicle_plate = get_vehicle_plate
    vehicles.get_vehicle_model = get_vehicle_model
    vehicles.get_doors_broken = get_doors_broken
    vehicles.get_windows_broken = get_windows_broken
    vehicles.get_tyre_burst = get_tyre_burst
    vehicles.get_vehicle_extras = get_vehicle_extras
    vehicles.get_custom_xenon_color = get_custom_xenon_color
    vehicles.get_vehicle_mod = get_vehicle_mod
    vehicles.get_vehicle_properties = get_vehicle_properties
    vehicles.get_vehicle_mods_and_maintenance = get_vehicle_mods_and_maintenance
    vehicles.get_vehicle_class = get_vehicle_class
    vehicles.get_vehicle_class_details = get_vehicle_class_details
    vehicles.get_vehicle_details = get_vehicle_details
    vehicles.spawn_vehicle = spawn_vehicle

    --- @section Exports

    exports('get_vehicle_plate', get_vehicle_plate)
    exports('get_vehicle_model', get_vehicle_model)
    exports('get_doors_broken', get_doors_broken)
    exports('get_windows_broken', get_windows_broken)
    exports('get_tyre_burst', get_tyre_burst)
    exports('get_vehicle_extras', get_vehicle_extras)
    exports('get_custom_xenon_color', get_custom_xenon_color)
    exports('get_vehicle_mod', get_vehicle_mod)
    exports('get_vehicle_properties', get_vehicle_properties)
    exports('get_vehicle_mods_and_maintenance', get_vehicle_mods_and_maintenance)
    exports('get_vehicle_class', get_vehicle_class)
    exports('get_vehicle_class_details', get_vehicle_class_details)
    exports('get_vehicle_details', get_vehicle_details)    
    exports('spawn_vehicle', spawn_vehicle)
```

## Version

***

### Server

```lua
    --- @section Function Assignments

    version.check = check_version

    --- @section Exports

    exports('check_version', check_version)
```

## XP

***

### Server

```lua
    --- @section Function Assignments

    xp.get_all = get_all_xp
    xp.get = get_xp
    xp.set = set_xp
    xp.add = add_xp
    xp.remove = remove_xp

    --- @section Exports

    exports('get_all_xp', get_all_xp)
    exports('get_xp', get_xp)
    exports('set_xp', set_xp)
    exports('add_xp', add_xp)
    exports('remove_xp', remove_xp)
```

### Client

```lua
    --- @section Function Assignments

    xp.get_all = get_all_xp

    --- @section Exports

    exports('get_all_xp', get_all_xp)
```


# Callbacks

The Callbacks module provides a standalone, framework-agnostic system for registering and triggering callbacks between the client and server.

***

## Accessing the Module

```lua
local CALLBACKS <const> = exports.boii_utils:get("modules.callbacks")
```

***

## Server

### register\_callback(name, cb)

Registers a server-side callback that clients can trigger.

### Parameters

| Name | Type       | Description                                             |
| ---- | ---------- | ------------------------------------------------------- |
| name | `string`   | The unique identifier for the callback event.           |
| cb   | `function` | The function to execute when the callback is triggered. |

### Example

```lua
CALLBACKS.register("get_server_time", function(source, data, cb)
    local response = {
        unix = os.time(),
        hour = GetClockHours()
    }
    cb(response)
end)
```

***

### Client

## trigger\_callback(name, data, cb)

Triggers a server-side callback and handles the result asynchronously.

### Parameters

| Name | Type       | Description                                                               |
| ---- | ---------- | ------------------------------------------------------------------------- |
| name | `string`   | The event name to trigger.                                                |
| data | `table`    | Optional data to send to the server.                                      |
| cb   | `function` | Callback function to handle the response. Signature: `function(response)` |

### Example

```lua
CALLBACKS.trigger("get_server_time", nil, function(response)
    print("Unix time:", response.unix)
    print("In-game hour:", response.hour)
end)
```


# Characters

The Characters module handles everything related to character customisation, including appearance styles, tattoos, clothing, and genetics. It is fully client-side and provides a consistent structure for defining, resetting, and applying player ped appearance.

***

## Accessing the Module

```lua
local CHARACTERS <const> = exports.boii_utils:get("modules.characters")
```

***

## Client

### get\_style(sex)

Retrieves the current appearance data structure for the given sex.

#### Parameters

| Name | Type     | Description                                             |
| ---- | -------- | ------------------------------------------------------- |
| sex  | `string` | Either `"m"` or `"f"` to get the appropriate structure. |

#### Example

```lua
local style = CHARACTERS.get_style("m")
print(style.clothing.jacket_style)
```

***

### reset\_styles()

Resets all style data back to defaults for both male and female presets.

#### Parameters

*None*

#### Example

```lua
CHARACTERS.reset_styles()
```

***

### get\_clothing\_and\_prop\_values(sex)

Returns maximum drawable/texture values for clothing and props for UI sliders.

#### Parameters

| Name | Type     | Description           |
| ---- | -------- | --------------------- |
| sex  | `string` | Either `"m"` or `"f"` |

#### Returns

A table of maximum drawable values.

#### Example

```lua
local values = CHARACTERS.get_clothing_and_prop_values("f")
print(values.hair, values.mask_texture)
```

***

### set\_ped\_appearance(player, data)

Applies the full style data (genetics, hair, clothing, etc.) to the specified ped.

#### Parameters

| Name   | Type     | Description                                |
| ------ | -------- | ------------------------------------------ |
| player | `number` | The `PlayerPedId()` to apply the style to. |
| data   | `table`  | The full character style data table.       |

#### Example

```lua
local ped = PlayerPedId()
local style = CHARACTERS.get_style("m")
CHARACTERS.set_ped_appearance(ped, style)
```

***

### update\_ped\_data(sex, category, id, value)

Updates a specific field in the ped style table and applies the changes to the current ped.

#### Parameters

| Name     | Type                | Description                                        |
| -------- | ------------------- | -------------------------------------------------- |
| sex      | `string`            | "m" or "f" to choose the dataset                   |
| category | `string`            | The sub-table: "genetics", "barber", or "clothing" |
| id       | `string`            | The field inside the category to update            |
| value    | `number` or `table` | The value to assign                                |

#### Example

```lua
CHARACTERS.update_ped_data("f", "barber", "hair", 6)
CHARACTERS.update_ped_data("f", "genetics", "eye_colour", 2)
```

***

### change\_player\_ped(sex)

Changes the current player's ped model and reapplies stored appearance.

#### Parameters

| Name | Type     | Description |
| ---- | -------- | ----------- |
| sex  | `string` | "m" or "f"  |

#### Example

```lua
CHARACTERS.change_player_ped("f")
```

***

### rotate\_ped(direction)

Rotates the current ped preview left, right, flip or reset.

#### Parameters

| Name      | Type     | Description                         |
| --------- | -------- | ----------------------------------- |
| direction | `string` | "right", "left", "flip", or "reset" |

#### Example

```lua
CHARACTERS.rotate_ped("left")
```

***

### load\_character\_model(data)

Sets the current player's model and applies the entire style set from saved character data.

#### Parameters

| Name | Type    | Description                                                      |
| ---- | ------- | ---------------------------------------------------------------- |
| data | `table` | The full identity and style structure used in character systems. |

#### Example

```lua
CHARACTERS.load_character_model({
    identity = { sex = "m" },
    style = CHARACTERS.get_style("m")
})
```

***

### Notes

* All functions are client-only
* Style tables are separated by sex (`m`, `f`)
* Update functions apply changes immediately to ped
* You can deep copy and modify style structures before applying them


# Commands

The Commands module provides a simple interface to register server commands with built-in permission checking and chat suggestions.

***

#### Accessing the Module

```lua
local COMMANDS <const> = exports.boii_utils:get("modules.commands")
```

***

## Server

### register\_command(command, required\_rank, help, params, handler)

Registers a new server command.

#### **Parameters**

| Name           | Type       | Description                                                                           |
| -------------- | ---------- | ------------------------------------------------------------------------------------- |
| command        | `string`   | The command name to register (e.g., `"/kick"`).                                       |
| required\_rank | \`string   | table\`                                                                               |
| help           | `string`   | Help text for the command (displayed in suggestions).                                 |
| params         | `table`    | Array of parameter objects with `name` and `help` fields.                             |
| handler        | `function` | Function to execute when the command is run. Signature: `function(source, args, raw)` |

#### **Example**

```lua
COMMANDS.register("announce", "admin", "Broadcast a message to all players", {
    { name = "message", help = "The message to broadcast" }
}, function(source, args, raw)
    local message = table.concat(args, " ")
    TriggerClientEvent("chat:addMessage", -1, {
        args = { "^3Announcement", message }
    })
end)
```

***

## Client

### get\_chat\_suggestions()

Requests the server to send updated chat suggestions to the client.

#### **Parameters**

| Name | Type | Description                        |
| ---- | ---- | ---------------------------------- |
| None | -    | This function takes no parameters. |

#### **Example**

```lua
COMMANDS.get_chat_suggestions()
```


# Cooldowns

The **Cooldowns module** provides a **standalone** system to handle **player, global, and resource-based cooldowns**. This ensures actions have enforced delays between executions.

## Server

### add\_cooldown(source, cooldown\_type, duration, is\_global)

Adds a cooldown for a player, globally, or for a specific resource.

#### Parameters:

* **source** *(number)* - The player's server ID (or identifier for non-global cooldowns).
* **cooldown\_type** *(string)* - The cooldown category (e.g., "begging").
* **duration** *(number)* - Duration of the cooldown in **seconds**.
* **is\_global** *(boolean)* - `true` for global cooldown, `false` for player-specific cooldown.

#### Example

```lua
local COOLDOWNS = exports.boii_utils:get("modules.cooldowns")
COOLDOWNS.add(1, "begging", 60, false) -- Adds a 60s cooldown for player 1
```

***

### check\_cooldown(source, cooldown\_type, is\_global)

Checks if a cooldown is active for a player or globally.

#### Parameters:

* **source** *(number)* - The player's server ID.
* **cooldown\_type** *(string)* - The cooldown category.
* **is\_global** *(boolean)* - `true` for global cooldown, `false` for player-specific cooldown.

#### Returns:

* *(boolean)* - `true` if cooldown is active, `false` otherwise.

#### Example

```lua
if COOLDOWNS.check(1, "begging", false) then
    print("Player is on cooldown.")
end
```

***

### clear\_cooldown(source, cooldown\_type, is\_global)

Clears an active cooldown for a player or globally.

#### Parameters:

* **source** *(number)* - The player's server ID.
* **cooldown\_type** *(string)* - The cooldown category.
* **is\_global** *(boolean)* - `true` for global cooldown, `false` for player-specific cooldown.

#### Example

```lua
COOLDOWNS.clear(1, "begging", false) -- Removes cooldown for player 1
```

***

### clear\_expired\_cooldowns()

Removes all expired cooldowns automatically.

#### Example

```lua
COOLDOWNS.clear_expired_cooldowns()
```

***

### clear\_resource\_cooldowns(resource\_name)

Clears all cooldowns set by a specific resource.

#### Parameters:

* **resource\_name** *(string)* - The name of the resource.

#### Example

```lua
COOLDOWNS.clear_resource_cooldowns("some_resource")
```


# Debugging

The Debugging module provides lightweight logging tools with color-coded output and timestamping, useful for both client and server contexts.

***

## Accessing the Module

```lua
local DEBUG <const> = exports.boii_utils:get("modules.debugging")
```

***

## Shared

### get\_current\_time()

Returns the current formatted time string.

#### Returns

| Type     | Description                                  |
| -------- | -------------------------------------------- |
| `string` | Current time in "YYYY-MM-DD HH:MM:SS" format |

#### Example

```lua
local time = DEBUG.get_current_time()
DEBUG.print("info", "Current time:", time)
```

***

### print(level, message, data?)

Logs a formatted message to the console with a colored prefix and optional table data. Automatically detects and prints the name of the invoking resource.

#### Parameters

| Name    | Type     | Description                                                |
| ------- | -------- | ---------------------------------------------------------- |
| level   | `string` | The log level: `debug`, `info`, `success`, `warn`, `error` |
| message | `string` | The message string to print                                |
| data?   | `table?` | Optional table to JSON encode and append to the message    |

#### Example

```lua
DEBUG.print("info", "Loading complete")
DEBUG.print("error", "Something went wrong", { code = 500, reason = "Bad request" })
```


# Entities

The `entities` module provides utility functions for detecting, filtering, and retrieving nearby or targeted game entities such as objects, peds, players, and vehicles.

***

## Accessing the Module

```lua
local ENTITIES <const> = exports.boii_utils:get("modules.entities")
```

***

## Client

### get\_nearby\_objects(coords, max\_distance)

Returns nearby objects within a certain distance.

#### Parameters

| Name          | Type      | Description                            |
| ------------- | --------- | -------------------------------------- |
| coords        | `vector3` | The center coordinates to search from. |
| max\_distance | `number`  | The maximum radius to search in.       |

#### Example

```lua
local nearby = ENTITIES.get_nearby_objects(GetEntityCoords(PlayerPedId()), 5.0)
for _, obj in pairs(nearby) do
    print("Nearby Object:", obj.entity, obj.coords)
end
```

***

### get\_nearby\_peds(coords, max\_distance)

Returns non-player peds nearby.

#### Parameters

| Name          | Type      | Description                            |
| ------------- | --------- | -------------------------------------- |
| coords        | `vector3` | The center position to search from.    |
| max\_distance | `number`  | The maximum distance to search within. |

#### Example

```lua
local peds = ENTITIES.get_nearby_peds(GetEntityCoords(PlayerPedId()), 10.0)
```

***

### get\_nearby\_players(coords, max\_distance, include\_player)

Returns player peds nearby.

#### Parameters

| Name            | Type      | Description                              |
| --------------- | --------- | ---------------------------------------- |
| coords          | `vector3` | The origin position to search from.      |
| max\_distance   | `number`  | How far out to search.                   |
| include\_player | `boolean` | Whether to include the executing player. |

#### Example

```lua
local players = ENTITIES.get_nearby_players(GetEntityCoords(PlayerPedId()), 10.0, false)
```

***

### get\_nearby\_vehicles(coords, max\_distance, include\_player\_vehicle)

Finds vehicles in proximity.

#### Parameters

| Name                     | Type      | Description                                             |
| ------------------------ | --------- | ------------------------------------------------------- |
| coords                   | `vector3` | Starting point for the check.                           |
| max\_distance            | `number`  | Max distance to scan.                                   |
| include\_player\_vehicle | `boolean` | Whether to include the player’s vehicle in the results. |

#### Example

```lua
local vehicles = ENTITIES.get_nearby_vehicles(GetEntityCoords(PlayerPedId()), 20.0, false)
```

***

### get\_closest\_object(coords, max\_distance)

Returns the closest object.

#### Parameters

| Name          | Type      | Description           |
| ------------- | --------- | --------------------- |
| coords        | `vector3` | Where to search from. |
| max\_distance | `number`  | Search radius.        |

#### Example

```lua
local object, coords = ENTITIES.get_closest_object(GetEntityCoords(PlayerPedId()), 5.0)
```

***

### get\_closest\_ped(coords, max\_distance)

Returns the nearest non-player ped.

#### Parameters

| Name          | Type      | Description            |
| ------------- | --------- | ---------------------- |
| coords        | `vector3` | Where to begin search. |
| max\_distance | `number`  | Distance limit.        |

#### Example

```lua
local ped, coords = ENTITIES.get_closest_ped(GetEntityCoords(PlayerPedId()), 10.0)
```

***

### get\_closest\_player(coords, max\_distance, include\_player)

Finds the nearest player ped.

#### Parameters

| Name            | Type      | Description                       |
| --------------- | --------- | --------------------------------- |
| coords          | `vector3` | Origin point.                     |
| max\_distance   | `number`  | Search distance.                  |
| include\_player | `boolean` | Include calling player in search. |

#### Example

```lua
local player, ped, coords = ENTITIES.get_closest_player(GetEntityCoords(PlayerPedId()), 10.0, false)
```

***

### get\_closest\_vehicle(coords, max\_distance, include\_player\_vehicle)

Returns the closest vehicle entity.

#### Parameters

| Name                     | Type      | Description                                       |
| ------------------------ | --------- | ------------------------------------------------- |
| coords                   | `vector3` | Coordinates to center the search on.              |
| max\_distance            | `number`  | Max scan distance.                                |
| include\_player\_vehicle | `boolean` | Whether to consider the player’s current vehicle. |

#### Example

```lua
local veh, coords = ENTITIES.get_closest_vehicle(GetEntityCoords(PlayerPedId()), 10.0, false)
```

***

### get\_entities\_in\_front\_of\_player(fov, distance)

Checks for an entity in the player’s FOV.

#### Parameters

| Name     | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| fov      | `number` | The angle cone to check in degrees. |
| distance | `number` | Max range to scan forward.          |

#### Example

```lua
local entity = ENTITIES.get_entities_in_front_of_player(45, 10.0)
```

***

### get\_target\_ped(player\_ped, fov, distance)

Finds a valid non-player ped in front or the nearest.

#### Parameters

| Name        | Type     | Description               |
| ----------- | -------- | ------------------------- |
| player\_ped | `number` | The calling player’s ped. |
| fov         | `number` | Angle to check forward.   |
| distance    | `number` | Maximum range to search.  |

#### Example

```lua
local ped, coords = ENTITIES.get_target_ped(PlayerPedId(), 60.0, 10.0)
```


# Environment

The Environment module provides utility functions to query and process in-game environmental data such as weather, time, season, terrain, and more.

***

## Accessing the Module

```lua
local ENVIRONMENT <const> = exports.boii_utils:get("modules.environment")
```

***

## Client

### get\_weather\_name(hash)

Returns the readable name of a weather type from its hash.

#### Parameters

| Name | Type     | Description                       |
| ---- | -------- | --------------------------------- |
| hash | `number` | The hash key of the weather type. |

#### Example

```lua
local weather = ENVIRONMENT.get_weather_name(GetPrevWeatherTypeHashName())
print("Current Weather:", weather)
```

***

### get\_game\_time()

Returns current game time (hour + minute) in both raw and formatted format.

#### Returns

| Name      | Type     | Description               |
| --------- | -------- | ------------------------- |
| time      | `table`  | `{ hour, minute }`        |
| formatted | `string` | Time formatted as `HH:MM` |

#### Example

```lua
local time = ENVIRONMENT.get_game_time()
print("Game Time:", time.formatted)
```

***

### get\_game\_date()

Returns current in-game date in both raw and formatted format.

#### Returns

| Name      | Type     | Description                    |
| --------- | -------- | ------------------------------ |
| date      | `table`  | `{ day, month, year }`         |
| formatted | `string` | Date formatted as `DD/MM/YYYY` |

#### Example

```lua
local date = ENVIRONMENT.get_game_date()
print("Game Date:", date.formatted)
```

***

### get\_sunrise\_sunset\_times(weather)

Returns sunrise and sunset times based on weather type.

#### Parameters

| Name    | Type     | Description                          |
| ------- | -------- | ------------------------------------ |
| weather | `string` | The weather type name (e.g. "CLEAR") |

#### Example

```lua
local times = ENVIRONMENT.get_sunrise_sunset_times("CLOUDS")
print("Sunrise:", times.sunrise, "Sunset:", times.sunset)
```

***

### is\_daytime()

Checks if the current time is between 06:00 and 18:00.

#### Returns

| Type      | Description                          |
| --------- | ------------------------------------ |
| `boolean` | `true` if daytime, otherwise `false` |

#### Example

```lua
if ENVIRONMENT.is_daytime() then
    print("It's daytime!")
end
```

***

### get\_current\_season()

Returns the current season based on in-game month.

#### Returns

| Type     | Description                                            |
| -------- | ------------------------------------------------------ |
| `string` | Season name: "Winter", "Spring", "Summer", or "Autumn" |

#### Example

```lua
local season = ENVIRONMENT.get_current_season()
print("Season:", season)
```

***

### get\_distance\_to\_water()

Calculates the vertical distance from player to nearest water surface.

#### Returns

| Type     | Description                                  |
| -------- | -------------------------------------------- |
| `number` | Distance to water or `-1` if no water nearby |

#### Example

```lua
local dist = ENVIRONMENT.get_distance_to_water()
print("Water Distance:", dist)
```

***

### get\_zone\_scumminess()

Returns the "scumminess" level of the player's current zone.

#### Returns

| Type      | Description                        |
| --------- | ---------------------------------- |
| `integer` | Value from 0-5, or -1 if not found |

#### Example

```lua
print("Zone Scumminess:", ENVIRONMENT.get_zone_scumminess())
```

***

### get\_ground\_material()

Returns the hash of the ground material at the player's feet.

#### Returns

| Type     | Description         |
| -------- | ------------------- |
| `number` | Material hash value |

#### Example

```lua
print("Ground Material Hash:", ENVIRONMENT.get_ground_material())
```

***

### get\_wind\_direction()

Returns the wind direction as a compass direction (N, NE, etc).

#### Returns

| Type     | Description       |
| -------- | ----------------- |
| `string` | Compass direction |

#### Example

```lua
print("Wind Direction:", ENVIRONMENT.get_wind_direction())
```

***

### get\_altitude()

Returns the player's current altitude above sea level.

#### Returns

| Type     | Description    |
| -------- | -------------- |
| `number` | Altitude value |

#### Example

```lua
print("Altitude:", ENVIRONMENT.get_altitude())
```

***

### get\_environment\_details()

Returns a detailed breakdown of the current environment.

#### Returns

| Key                 | Type      | Description               |
| ------------------- | --------- | ------------------------- |
| weather             | `string`  | Current weather name      |
| time                | `table`   | Table of game time info   |
| date                | `table`   | Table of game date info   |
| season              | `string`  | Current season            |
| sunrise\_sunset     | `table`   | Sunrise/sunset times      |
| is\_daytime         | `boolean` | True if daytime           |
| distance\_to\_water | `number`  | Distance to nearest water |
| scumminess          | `number`  | Zone scumminess level     |
| ground\_material    | `number`  | Ground hash               |
| rain\_level         | `number`  | Current rain amount       |
| wind\_speed         | `number`  | Current wind speed        |
| wind\_direction     | `string`  | Compass wind direction    |
| snow\_level         | `number`  | Current snow amount       |
| altitude            | `number`  | Player's altitude         |

#### Example

```lua
local env = ENVIRONMENT.get_environment_details()
print(json.encode(env))
```


# Framework Bridge

The Core Bridge module provides a unified API across multiple frameworks for player data, identity, inventory, balances, jobs, and more. The examples below reflect the boii\_core implementation, but the API remains consistent across all supported frameworks.

***

## Accessing the Module

```lua
local CORE <const> = exports.boii_utils:get("bridges.framework")
```

***

## Server

### get\_players()

Returns all players connected to the server.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| -    | -    | None        |

#### Example

```lua
local players = CORE.get_players()
```

***

### get\_player(source)

Retrieves player data by source ID.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Example

```lua
local player = CORE.get_player(source)
```

***

### get\_id\_params(source)

Generates identifier query and parameters.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Example

```lua
local query, params = CORE.get_id_params(source)
```

***

### get\_player\_id(source)

Returns the player's main identifier.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Example

```lua
local id = CORE.get_player_id(source)
```

***

### get\_identity(source)

Returns a player's identity information.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Example

```lua
local identity = CORE.get_identity(source)
```

***

### get\_inventory(source)

Gets a player's inventory.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Example

```lua
local inventory = CORE.get_inventory(source)
```

***

### get\_item(source, item\_name)

Gets a specific item from the player's inventory.

#### Parameters

| Name       | Type     | Description      |
| ---------- | -------- | ---------------- |
| source     | `number` | Player source ID |
| item\_name | `string` | Name of the item |

#### Example

```lua
local item = CORE.get_item(source, "radio")
```

***

### has\_item(source, item\_name, item\_amount?)

Checks if a player has an item in their inventory.

#### Parameters

| Name          | Type     | Description                    |
| ------------- | -------- | ------------------------------ |
| source        | `number` | Player source ID               |
| item\_name    | `string` | Name of the item               |
| item\_amount? | `number` | Optional quantity (default: 1) |

#### Example

```lua
if CORE.has_item(source, "bandage", 2) then
    -- has at least 2 bandages
end
```

***

### add\_item(source, item\_id, amount, data?)

Adds an item to a player's inventory.

#### Parameters

| Name     | Type     | Description                   |
| -------- | -------- | ----------------------------- |
| source   | `number` | Player source ID              |
| item\_id | `string` | ID of the item                |
| amount   | `number` | Quantity                      |
| data?    | `table`  | Optional metadata (e.g. ammo) |

#### Example

```lua
CORE.add_item(source, "ammo_9mm", 50)
```

***

### remove\_item(source, item\_id, amount)

Removes an item from a player's inventory.

#### Parameters

| Name     | Type     | Description        |
| -------- | -------- | ------------------ |
| source   | `number` | Player source ID   |
| item\_id | `string` | ID of the item     |
| amount   | `number` | Quantity to remove |

#### Example

```lua
CORE.remove_item(source, "radio", 1)
```

***

### update\_item\_data(source, item\_id, updates)

Modifies an item entry (e.g. ammo or durability).

#### Parameters

| Name     | Type     | Description               |
| -------- | -------- | ------------------------- |
| source   | `number` | Player source ID          |
| item\_id | `string` | ID of the item            |
| updates  | `table`  | Data to apply (key/value) |

#### Example

```lua
CORE.update_item_data(source, "weapon_pistol", { durability = 95 })
```

***

### get\_balances(source)

Returns all account balances.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Example

```lua
local balances = CORE.get_balances(source)
```

***

### get\_balance\_by\_type(source, balance\_type)

Gets a specific account balance.

#### Parameters

| Name          | Type     | Description          |
| ------------- | -------- | -------------------- |
| source        | `number` | Player source ID     |
| balance\_type | `string` | Type: "cash", "bank" |

#### Example

```lua
local cash = CORE.get_balance_by_type(source, "cash")
```

***

### add\_balance(source, balance\_type, amount, sender?, note?)

Adds money to a player account.

#### Parameters

| Name          | Type     | Description                 |
| ------------- | -------- | --------------------------- |
| source        | `number` | Player source ID            |
| balance\_type | `string` | Account type                |
| amount        | `number` | Amount to add               |
| sender?       | `string` | Optional sender description |
| note?         | `string` | Optional transaction note   |

#### Example

```lua
CORE.add_balance(source, "bank", 1000, "ATM", "Paycheck")
```

***

### remove\_balance(source, balance\_type, amount, recipient?, note?)

Removes money from a player account.

#### Parameters

| Name          | Type     | Description               |
| ------------- | -------- | ------------------------- |
| source        | `number` | Player source ID          |
| balance\_type | `string` | Account type              |
| amount        | `number` | Amount to remove          |
| recipient?    | `string` | Optional recipient name   |
| note?         | `string` | Optional transaction note |

#### Example

```lua
CORE.remove_balance(source, "cash", 250)
```

***

### get\_player\_jobs(source)

Returns a list of jobs the player currently holds.

#### Parameters

| Name   | Type     | Description              |
| ------ | -------- | ------------------------ |
| source | `number` | Player source identifier |

#### Example

```lua
local jobs = CORE.get_player_jobs(source)
for _, job in pairs(jobs) do
    print("Job:", job)
end
```

***

### player\_has\_job(source, job\_names, check\_on\_duty?)

Checks whether a player has one of the specified jobs. Optionally checks if they're on duty.

#### Parameters

| Name            | Type       | Description                       |
| --------------- | ---------- | --------------------------------- |
| source          | `number`   | Player source identifier          |
| job\_names      | `table`    | List of job names to check        |
| check\_on\_duty | `boolean?` | Whether to require on-duty status |

#### Example

```lua
local hasJob = CORE.player_has_job(source, { "police" }, true)
```

***

### get\_player\_job\_grade(source, job\_id)

Returns the player's rank (grade) for the specified job.

#### Parameters

| Name    | Type     | Description              |
| ------- | -------- | ------------------------ |
| source  | `number` | Player source identifier |
| job\_id | `string` | Job ID to check          |

#### Example

```lua
local grade = CORE.get_player_job_grade(source, "police")
```

***

### count\_players\_by\_job(job\_names, check\_on\_duty?)

Counts players with a specific job. Optionally checks duty status.

#### Parameters

| Name            | Type       | Description                       |
| --------------- | ---------- | --------------------------------- |
| job\_names      | `table`    | List of job names to count        |
| check\_on\_duty | `boolean?` | Whether to check for on-duty only |

#### Example

```lua
local total, onduty = CORE.count_players_by_job({ "ambulance" }, true)
```

***

### get\_player\_job\_name(source)

Returns the first job name assigned to a player.

#### Parameters

| Name   | Type     | Description              |
| ------ | -------- | ------------------------ |
| source | `number` | Player source identifier |

#### Example

```lua
local job = CORE.get_player_job_name(source)
```

***

### adjust\_statuses(source, statuses)

Applies status modifications to a player server-side.

#### Parameters

| Name     | Type     | Description              |
| -------- | -------- | ------------------------ |
| source   | `number` | Player source identifier |
| statuses | `table`  | Table of status values   |

#### Example

```lua
CORE.adjust_statuses(source, { hunger = -10, thirst = -5 })
```

***

### register\_item(item, cb)

Registers an item as usable and calls the callback on use.

#### Parameters

| Name | Type       | Description                   |
| ---- | ---------- | ----------------------------- |
| item | `string`   | Name of the usable item       |
| cb   | `function` | Function to run on item usage |

#### Example

```lua
CORE.register_item("joint", function(source)
    print("Joint used by:", source)
end)
```

***

## Client

### get\_data(key?)

Returns the full or partial player data table.

#### Parameters

| Name | Type     | Description                    |
| ---- | -------- | ------------------------------ |
| key  | `string` | (Optional) Specific key to get |

#### Example

```lua
local data = CORE.get_data("identity")
```

***

### get\_identity()

Returns a structured identity object.

#### Example

```lua
local id = CORE.get_identity()
print("Name:", id.first_name, id.last_name)
```

***

### get\_player\_id()

Returns the current player's unique identifier.

#### Example

```lua
local player_id = CORE.get_player_id()
```


# Geometry

### Geometry Module

Provides utility functions for performing geometric and spatial calculations in 2D and 3D environments.

***

## Accessing the Module

```lua
local GEOMETRY <const> = exports.boii_utils:get("modules.geometry")
```

***

## Shared

### distance\_2d(p1, p2)

Calculates the distance between two 2D points.

#### Parameters

| Name | Type    | Description           |
| ---- | ------- | --------------------- |
| p1   | `table` | First point `{x, y}`  |
| p2   | `table` | Second point `{x, y}` |

#### Example

```lua
local d = GEOMETRY.distance_2d({x = 0, y = 0}, {x = 3, y = 4})
-- d = 5
```

***

### distance\_3d(p1, p2)

Calculates the distance between two 3D points.

#### Parameters

| Name | Type    | Description              |
| ---- | ------- | ------------------------ |
| p1   | `table` | First point `{x, y, z}`  |
| p2   | `table` | Second point `{x, y, z}` |

#### Example

```lua
local d = GEOMETRY.distance_3d({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 10})
-- d = 10
```

***

### midpoint(p1, p2)

Returns the midpoint between two 3D points.

#### Parameters

| Name | Type    | Description              |
| ---- | ------- | ------------------------ |
| p1   | `table` | First point `{x, y, z}`  |
| p2   | `table` | Second point `{x, y, z}` |

#### Example

```lua
local mid = GEOMETRY.midpoint({x = 0, y = 0, z = 0}, {x = 4, y = 4, z = 4})
-- mid = {x = 2, y = 2, z = 2}
```

***

### is\_point\_in\_rect(point, rect)

Checks if a point is within a 2D rectangle.

#### Parameters

| Name  | Type    | Description                           |
| ----- | ------- | ------------------------------------- |
| point | `table` | The point `{x, y}`                    |
| rect  | `table` | The rectangle `{x, y, width, height}` |

#### Example

```lua
local inside = GEOMETRY.is_point_in_rect({x = 5, y = 5}, {x = 0, y = 0, width = 10, height = 10})
-- inside = true
```

***

### is\_point\_in\_box(point, box)

Checks if a 3D point is within a box.

#### Parameters

| Name  | Type    | Description                               |
| ----- | ------- | ----------------------------------------- |
| point | `table` | The point `{x, y, z}`                     |
| box   | `table` | The box `{x, y, z, width, height, depth}` |

#### Example

```lua
local inside = GEOMETRY.is_point_in_box({x = 2, y = 2, z = 2}, {x = 0, y = 0, z = 0, width = 5, height = 5, depth = 5})
```

***

### is\_point\_on\_line\_segment(point, line\_start, line\_end)

Checks if a 2D point lies on a line segment.

#### Parameters

| Name        | Type    | Description                 |
| ----------- | ------- | --------------------------- |
| point       | `table` | The point `{x, y}`          |
| line\_start | `table` | Line segment start `{x, y}` |
| line\_end   | `table` | Line segment end `{x, y}`   |

#### Example

```lua
local result = GEOMETRY.is_point_on_line_segment({x = 2, y = 2}, {x = 0, y = 0}, {x = 4, y = 4})
```

***

### project\_point\_on\_line(p, p1, p2)

Projects a point onto a 2D line segment.

#### Parameters

| Name | Type    | Description         |
| ---- | ------- | ------------------- |
| p    | `table` | Point `{x, y}`      |
| p1   | `table` | Line start `{x, y}` |
| p2   | `table` | Line end `{x, y}`   |

#### Example

```lua
local projected = GEOMETRY.project_point_on_line({x = 3, y = 4}, {x = 0, y = 0}, {x = 5, y = 0}) -- projected = {x = 3, y = 0}
```

***

### calculate\_slope(p1, p2)

Calculates the slope of a line between two 2D points.

#### Parameters

| Name | Type    | Description          |
| ---- | ------- | -------------------- |
| p1   | `table` | First point `{x,y}`  |
| p2   | `table` | Second point `{x,y}` |

#### Example

```lua
local slope = GEOMETRY.calculate_slope({x = 1, y = 2}, {x = 4, y = 6}) -- slope = 1.333
```

### angle\_between\_points(p1, p2)

Returns the angle between two 2D points in degrees.

#### Parameters

| Name | Type    | Description               |
| ---- | ------- | ------------------------- |
| p1   | `table` | The first point `{x, y}`  |
| p2   | `table` | The second point `{x, y}` |

#### Example

```lua
local angle = GEOMETRY.angle_between_points({ x = 0, y = 0 }, { x = 1, y = 1 })
```

***

### angle\_between\_3\_points(p1, p2, p3)

Calculates the angle between three 3D points (with `p2` as the vertex).

#### Parameters

| Name | Type    | Description                     |
| ---- | ------- | ------------------------------- |
| p1   | `table` | First point `{x, y, z}`         |
| p2   | `table` | Center/vertex point `{x, y, z}` |
| p3   | `table` | Third point `{x, y, z}`         |

#### Example

```lua
local angle = GEOMETRY.angle_between_3_points(p1, p2, p3)
```

***

### do\_circles\_intersect(c1\_center, c1\_radius, c2\_center, c2\_radius)

Checks if two 2D circles intersect.

#### Parameters

| Name       | Type     | Description                      |
| ---------- | -------- | -------------------------------- |
| c1\_center | `table`  | Center of first circle `{x, y}`  |
| c1\_radius | `number` | Radius of first circle           |
| c2\_center | `table`  | Center of second circle `{x, y}` |
| c2\_radius | `number` | Radius of second circle          |

#### Example

```lua
local result = GEOMETRY.do_circles_intersect({ x = 0, y = 0 }, 5, { x = 3, y = 0 }, 5)
```

***

### is\_point\_in\_circle(point, circle\_center, circle\_radius)

Checks if a 2D point is inside a circle.

#### Parameters

| Name           | Type     | Description                 |
| -------------- | -------- | --------------------------- |
| point          | `table`  | The point to check `{x, y}` |
| circle\_center | `table`  | Circle center `{x, y}`      |
| circle\_radius | `number` | Radius of the circle        |

#### Example

```lua
local inside = GEOMETRY.is_point_in_circle({ x = 2, y = 2 }, { x = 0, y = 0 }, 5)
```

***

### do\_lines\_intersect(l1\_start, l1\_end, l2\_start, l2\_end)

Determines whether two 2D line segments intersect.

#### Parameters

| Name      | Type    | Description              |
| --------- | ------- | ------------------------ |
| l1\_start | `table` | Start of line 1 `{x, y}` |
| l1\_end   | `table` | End of line 1 `{x, y}`   |
| l2\_start | `table` | Start of line 2 `{x, y}` |
| l2\_end   | `table` | End of line 2 `{x, y}`   |

#### Example

```lua
local intersects = GEOMETRY.do_lines_intersect(p1, p2, p3, p4)
```

***

### line\_intersects\_circle(line\_start, line\_end, circle\_center, circle\_radius)

Checks if a line segment intersects with a circle.

#### Parameters

| Name           | Type     | Description            |
| -------------- | -------- | ---------------------- |
| line\_start    | `table`  | Line start `{x, y}`    |
| line\_end      | `table`  | Line end `{x, y}`      |
| circle\_center | `table`  | Circle center `{x, y}` |
| circle\_radius | `number` | Radius of circle       |

#### Example

```lua
local hit = GEOMETRY.line_intersects_circle(p1, p2, { x = 0, y = 0 }, 10)
```

***

### does\_rect\_intersect\_line(rect, line\_start, line\_end)

Checks if a 2D rectangle intersects with a line segment.

#### Parameters

| Name        | Type    | Description                       |
| ----------- | ------- | --------------------------------- |
| rect        | `table` | Rectangle `{x, y, width, height}` |
| line\_start | `table` | Line start `{x, y}`               |
| line\_end   | `table` | Line end `{x, y}`                 |

#### Example

```lua
local crosses = GEOMETRY.does_rect_intersect_line(rect, p1, p2)
```

***

### closest\_point\_on\_line\_segment(point, line\_start, line\_end)

Returns the closest point on a line segment to a given point.

#### Parameters

| Name        | Type    | Description                 |
| ----------- | ------- | --------------------------- |
| point       | `table` | The point to check `{x, y}` |
| line\_start | `table` | Line start `{x, y}`         |
| line\_end   | `table` | Line end `{x, y}`           |

#### Example

```lua
local closest = GEOMETRY.closest_point_on_line_segment({ x = 2, y = 2 }, p1, p2)
```

***

### triangle\_area\_3d(p1, p2, p3)

Returns the area of a 3D triangle given three vertices.

#### Parameters

| Name | Type    | Description          |
| ---- | ------- | -------------------- |
| p1   | `table` | Vertex 1 `{x, y, z}` |
| p2   | `table` | Vertex 2 `{x, y, z}` |
| p3   | `table` | Vertex 3 `{x, y, z}` |

#### Example

```lua
local area = GEOMETRY.triangle_area_3d(p1, p2, p3)
```

***

### is\_point\_in\_sphere(point, sphere\_center, sphere\_radius)

Checks if a 3D point lies inside a sphere.

#### Parameters

| Name           | Type     | Description               |
| -------------- | -------- | ------------------------- |
| point          | `table`  | `{x, y, z}`               |
| sphere\_center | `table`  | Sphere center `{x, y, z}` |
| sphere\_radius | `number` | Radius of the sphere      |

#### Example

```lua
local inside = GEOMETRY.is_point_in_sphere(pos, center, 10)
```

***

### do\_spheres\_intersect(s1\_center, s1\_radius, s2\_center, s2\_radius)

Checks if two spheres intersect.

#### Parameters

| Name       | Type     | Description               |
| ---------- | -------- | ------------------------- |
| s1\_center | `table`  | Sphere 1 center `{x,y,z}` |
| s1\_radius | `number` | Sphere 1 radius           |
| s2\_center | `table`  | Sphere 2 center `{x,y,z}` |
| s2\_radius | `number` | Sphere 2 radius           |

#### Example

```lua
local hit = GEOMETRY.do_spheres_intersect(c1, 5, c2, 6)
```

***

### is\_point\_in\_convex\_polygon(point, polygon)

Checks if a 2D point lies inside a convex polygon.

#### Parameters

| Name    | Type    | Description                            |
| ------- | ------- | -------------------------------------- |
| point   | `table` | `{x, y}`                               |
| polygon | `table` | List of points `{ {x,y}, {x,y}, ... }` |

#### Example

```lua
local inside = GEOMETRY.is_point_in_convex_polygon({ x = 2, y = 3 }, polygon)
```

***

### rotate\_point\_around\_point\_2d(point, pivot, angle\_degrees)

Rotates a point around another point by degrees.

#### Parameters

| Name           | Type     | Description              |
| -------------- | -------- | ------------------------ |
| point          | `table`  | `{x, y}`                 |
| pivot          | `table`  | Rotation center `{x, y}` |
| angle\_degrees | `number` | Degrees to rotate        |

#### Example

```lua
local result = GEOMETRY.rotate_point_around_point_2d({x = 3, y = 3}, {x = 0, y = 0}, 90)
```

***

### distance\_point\_to\_plane(point, plane\_point, plane\_normal)

Calculates the shortest distance from a point to a plane.

#### Parameters

| Name          | Type    | Description         |
| ------------- | ------- | ------------------- |
| point         | `table` | `{x, y, z}`         |
| plane\_point  | `table` | Point on the plane  |
| plane\_normal | `table` | Normal of the plane |

#### Example

```lua
local dist = GEOMETRY.distance_point_to_plane(p, plane_p, normal)
```

***

### rotation\_to\_direction(rotation)

Converts Euler angles into a directional vector.

#### Parameters

| Name     | Type    | Description                     |
| -------- | ------- | ------------------------------- |
| rotation | `table` | Euler rotation `{x, y, z}` in ° |

#### Example

```lua
local dir = GEOMETRY.rotation_to_direction({x = 0, y = 0, z = 90})
```

***

### rotate\_box(center, width, length, heading)

Returns the 4 rotated corners of a box in 3D.

#### Parameters

| Name    | Type     | Description               |
| ------- | -------- | ------------------------- |
| center  | `table`  | Center of box `{x, y, z}` |
| width   | `number` | Box width                 |
| length  | `number` | Box length                |
| heading | `number` | Heading in degrees        |

#### Example

```lua
local corners = GEOMETRY.rotate_box({x=0,y=0,z=0}, 4, 2, 45)
```

***

### calculate\_rotation\_matrix(heading, pitch, roll)

Returns a basic Z rotation matrix.

#### Parameters

| Name    | Type     | Description        |
| ------- | -------- | ------------------ |
| heading | `number` | Heading in degrees |
| pitch   | `number` | Pitch in degrees   |
| roll    | `number` | Roll in degrees    |

#### Example

```lua
local matrix = GEOMETRY.calculate_rotation_matrix(90, 0, 0)
```

***

### translate\_point\_to\_local\_space(point, box\_origin, rot\_matrix)

Converts world point into local box space.

#### Parameters

| Name        | Type    | Description         |
| ----------- | ------- | ------------------- |
| point       | `table` | Point `{x, y, z}`   |
| box\_origin | `table` | Origin `{x, y, z}`  |
| rot\_matrix | `table` | 3x3 rotation matrix |

#### Example

```lua
local local_point = GEOMETRY.translate_point_to_local_space(p, origin, matrix)
```

***

### is\_point\_in\_oriented\_box(point, box)

Checks if a point is inside a rotated 3D box.

#### Parameters

| Name  | Type    | Description                                                                           |
| ----- | ------- | ------------------------------------------------------------------------------------- |
| point | `table` | The point `{x, y, z}`                                                                 |
| box   | `table` | Box config with keys `coords`, `width`, `height`, `depth`, `heading`, `pitch`, `roll` |

#### Example

```lua
local inside = GEOMETRY.is_point_in_oriented_box(pos, box)
```


# Items

Provides a way to register and trigger usable item logic server-side. This module allows developers to define behaviour when an item is used in-game.

***

## Accessing the Module

```lua
local ITEMS <const> = exports.boii_utils:get("modules.items")
```

***

## Server

### register\_item(item\_id, use\_function)

Registers an item as usable. When the item is used, the callback function will be invoked with the source and item ID.

#### Parameters

| Name          | Type       | Description                        |
| ------------- | ---------- | ---------------------------------- |
| item\_id      | `string`   | The item identifier                |
| use\_function | `function` | The function to call on item usage |

#### Example

```lua
ITEMS.register_item("firstaid", function(source, item_id)
    print("Used item:", item_id, "by player:", source)
end)
```

***

### use\_item(source, item\_id)

Triggers a previously registered usable item for the player.

#### Parameters

| Name     | Type     | Description                  |
| -------- | -------- | ---------------------------- |
| source   | `number` | The player source identifier |
| item\_id | `string` | The item identifier to use   |

#### Example

```lua
ITEMS.use_item(source, "firstaid")
```


# Keys

Provides functions for working with key names and codes.

***

## Accessing the Module

```lua
local KEYS <const> = exports.boii_utils:get("modules.keys")
```

***

## Shared

### get\_keys()

Returns the full key list as a table.

#### Parameters

*None*

#### Returns

* `table`: A mapping of key names to key codes.

#### Example

```lua
local all_keys = KEYS.get_keys()
print(all_keys["e"]) -- 46
```

***

### get\_key(key\_name)

Gets the key code for a given key name.

#### Parameters

| Name      | Type     | Description                |
| --------- | -------- | -------------------------- |
| key\_name | `string` | Name of the key (e.g. "e") |

#### Returns

* `number|nil`: Key code if found, or `nil` if not.

#### Example

```lua
local code = KEYS.get_key("f")
if code then
    print("Key code for F is:", code)
end
```

***

### get\_key\_name(key\_code)

Gets the key name for a given key code.

#### Parameters

| Name      | Type     | Description         |
| --------- | -------- | ------------------- |
| key\_code | `number` | The code to look up |

#### Returns

* `string|nil`: The key name if found, or `nil` if not.

#### Example

```lua
local name = KEYS.get_key_name(244)
if name then
    print("Key 244 is:", name)
end
```

***

### print\_key\_list()

Prints the full list of key names and codes to the console.

#### Parameters

*None*

#### Returns

*None*

#### Example

```lua
KEYS.print_key_list()
```

***

### key\_exists(key\_name)

Checks if a given key name exists in the key table.

#### Parameters

| Name      | Type     | Description       |
| --------- | -------- | ----------------- |
| key\_name | `string` | Key name to check |

#### Returns

* `boolean`: `true` if the key exists, `false` otherwise.

#### Example

```lua
if KEYS.key_exists("enter") then
    print("Enter key exists")
end
```


# Licences

Provides a standalone licence system supporting driving, weapon, hunting licences and more, with support for points and revocation. Framework-agnostic.

***

## Accessing the Module

```lua
local LICENCES <const> = exports.boii_utils:get("modules.licences")
```

***

## Server

### get\_licences(source)

Retrieves all licences for a player.

#### Parameters

| Name   | Type     | Description              |
| ------ | -------- | ------------------------ |
| source | `number` | Player source identifier |

#### Example

```lua
local all_licences = LICENCES.get_all(source)
```

***

### get\_licence(source, licence\_id)

Retrieves a specific licence for a player.

#### Parameters

| Name        | Type     | Description              |
| ----------- | -------- | ------------------------ |
| source      | `number` | Player source identifier |
| licence\_id | `string` | Licence ID               |

#### Example

```lua
local weapon_licence = LICENCES.get(source, "weapon")
```

***

### add\_licence(source, licence\_id)

Grants a player a new licence.

#### Parameters

| Name        | Type     | Description       |
| ----------- | -------- | ----------------- |
| source      | `number` | Player source ID  |
| licence\_id | `string` | Licence ID to add |

#### Example

```lua
LICENCES.add(source, "driver")
```

***

### remove\_licence(source, licence\_id)

Removes a player's licence.

#### Parameters

| Name        | Type     | Description          |
| ----------- | -------- | -------------------- |
| source      | `number` | Player source ID     |
| licence\_id | `string` | Licence ID to remove |

#### Example

```lua
LICENCES.remove(source, "driver")
```

***

### add\_points(source, licence\_id, points)

Adds penalty points to a player's licence.

#### Parameters

| Name        | Type     | Description          |
| ----------- | -------- | -------------------- |
| source      | `number` | Player source ID     |
| licence\_id | `string` | Licence ID to modify |
| points      | `number` | Points to add        |

#### Example

```lua
LICENCES.add_points(source, "driver", 3)
```

***

### remove\_points(source, licence\_id, points)

Removes penalty points from a player's licence.

#### Parameters

| Name        | Type     | Description          |
| ----------- | -------- | -------------------- |
| source      | `number` | Player source ID     |
| licence\_id | `string` | Licence ID to modify |
| points      | `number` | Points to remove     |

#### Example

```lua
LICENCES.remove_points(source, "driver", 2)
```

***

### update\_licence(source, licence\_id, test\_type, passed)

Updates a licence to mark theory or practical passed.

#### Parameters

| Name        | Type      | Description                        |
| ----------- | --------- | ---------------------------------- |
| source      | `number`  | Player source ID                   |
| licence\_id | `string`  | Licence ID to update               |
| test\_type  | `string`  | Either `"theory"` or `"practical"` |
| passed      | `boolean` | `true` if passed, `false` if not   |

#### Example

```lua
LICENCES.update(source, "driver", "practical", true)
```

***

## Client

### get\_licences()

Triggers server callback to fetch current player's licences.

#### Parameters

None

#### Example

```lua
LICENCES.get_all(function(data)
    print(json.encode(data))
end)
```


# Maths

Provides mathematical utility functions not covered by Lua's built-in math library.

***

## Accessing the Module

```lua
local MATHS <const> = exports.boii_utils:get("modules.maths")
```

***

## Shared

### round(number, decimals)

Rounds a number to a specified number of decimal places.

#### Parameters

| Name     | Type     | Description                          |
| -------- | -------- | ------------------------------------ |
| number   | `number` | The number to round                  |
| decimals | `number` | Number of decimal places to round to |

#### Example

```lua
local result = MATHS.round(3.14159, 2) -- 3.14
```

***

### calculate\_distance(start\_coords, end\_coords)

Calculates the 3D distance between two points.

#### Parameters

| Name          | Type    | Description             |
| ------------- | ------- | ----------------------- |
| start\_coords | `table` | Start point `{x, y, z}` |
| end\_coords   | `table` | End point `{x, y, z}`   |

#### Example

```lua
local dist = MATHS.calculate_distance({x=0,y=0,z=0}, {x=3,y=4,z=0}) -- 5.0
```

***

### clamp(val, lower, upper)

Clamps a number within a given range.

#### Parameters

| Name  | Type     | Description    |
| ----- | -------- | -------------- |
| val   | `number` | Value to clamp |
| lower | `number` | Minimum value  |
| upper | `number` | Maximum value  |

#### Example

```lua
local result = MATHS.clamp(15, 0, 10) -- 10
```

***

### lerp(a, b, t)

Performs linear interpolation between two numbers.

#### Parameters

| Name | Type     | Description                    |
| ---- | -------- | ------------------------------ |
| a    | `number` | Start value                    |
| b    | `number` | End value                      |
| t    | `number` | Interpolation factor (0.0-1.0) |

#### Example

```lua
local result = MATHS.lerp(0, 100, 0.5) -- 50
```

***

### factorial(n)

Calculates the factorial of a number.

#### Parameters

| Name | Type     | Description         |
| ---- | -------- | ------------------- |
| n    | `number` | Number to factorial |

#### Example

```lua
local fact = MATHS.factorial(5) -- 120
```

***

### deg\_to\_rad(deg)

Converts degrees to radians.

#### Parameters

| Name | Type     | Description      |
| ---- | -------- | ---------------- |
| deg  | `number` | Angle in degrees |

#### Example

```lua
local rad = MATHS.deg_to_rad(180) -- 3.1415...
```

***

### rad\_to\_deg(rad)

Converts radians to degrees.

#### Parameters

| Name | Type     | Description      |
| ---- | -------- | ---------------- |
| rad  | `number` | Angle in radians |

#### Example

```lua
local deg = MATHS.rad_to_deg(math.pi) -- 180
```

***

### circle\_circumference(radius)

Returns circumference of a circle.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| radius | `number` | Radius of circle |

#### Example

```lua
local circ = MATHS.circle_circumference(10) -- 62.83...
```

***

### circle\_area(radius)

Returns area of a circle.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| radius | `number` | Radius of circle |

#### Example

```lua
local area = MATHS.circle_area(5) -- 78.539...
```

***

### triangle\_area(p1, p2, p3)

Calculates area of triangle from 2D points.

#### Parameters

| Name | Type    | Description    |
| ---- | ------- | -------------- |
| p1   | `table` | Point `{x, y}` |
| p2   | `table` | Point `{x, y}` |
| p3   | `table` | Point `{x, y}` |

#### Example

```lua
local area = MATHS.triangle_area(p1, p2, p3)
```

***

### mean(numbers)

Returns average of number list.

#### Parameters

| Name    | Type    | Description     |
| ------- | ------- | --------------- |
| numbers | `table` | List of numbers |

#### Example

```lua
local avg = MATHS.mean({1,2,3,4,5}) -- 3
```

***

### median(numbers)

Returns median of list.

#### Parameters

| Name    | Type    | Description     |
| ------- | ------- | --------------- |
| numbers | `table` | List of numbers |

#### Example

```lua
local m = MATHS.median({5, 2, 1, 4, 3}) -- 3
```

***

### mode(numbers)

Returns most frequent number.

#### Parameters

| Name    | Type    | Description     |
| ------- | ------- | --------------- |
| numbers | `table` | List of numbers |

#### Example

```lua
local m = MATHS.mode({1, 2, 2, 3, 4}) -- 2
```

***

### standard\_deviation(numbers)

Returns standard deviation.

#### Parameters

| Name    | Type    | Description     |
| ------- | ------- | --------------- |
| numbers | `table` | List of numbers |

#### Example

```lua
local std = MATHS.standard_deviation({1,2,3,4,5})
```

***

### linear\_regression(points)

Returns slope + intercept for set of 2D points.

#### Parameters

| Name   | Type    | Description             |
| ------ | ------- | ----------------------- |
| points | `table` | List of `{x, y}` points |

#### Example

```lua
local result = MATHS.linear_regression({{x=1,y=2},{x=2,y=4},{x=3,y=6}})
print(result.slope, result.intercept)
```


# Methods

The `methods` module provides a system to register, remove, and trigger custom method callbacks on both the client and server.\
These are useful for extending resource behavior without modifying the core logic.

***

### Accessing the Module

```lua
local METHODS <const> = exports.boii_utils:get("modules.methods")
```

***

## Shared

### add\_method(event\_name, cb, options?)

Adds a method callback to a specific event.

#### Parameters

| Name        | Type     | Description                               |
| ----------- | -------- | ----------------------------------------- |
| event\_name | string   | Name of the event to hook into.           |
| cb          | function | Function to call when the event triggers. |
| options?    | table    | Optional table for method filtering/data. |

#### Returns

| Type   | Description                         |
| ------ | ----------------------------------- |
| number | ID of the method for later removal. |

#### Example

```lua
METHODS.add("on_some_event", function(data)
    if data.thing == "important" then
        print("Handled important thing")
    end
end)
```

***

### remove\_method(event\_name, id)

Removes a method callback from an event.

#### Parameters

| Name        | Type   | Description                      |
| ----------- | ------ | -------------------------------- |
| event\_name | string | The event name to remove from.   |
| id          | number | ID returned from `add_method()`. |

#### Example

```lua
local id = METHODS.add("custom_event", function(data) end)
METHODS.remove("custom_event", id)
```

***

### trigger\_method(event\_name, response)

Triggers all registered methods for an event. If any method returns `false`, execution halts.

#### Parameters

| Name        | Type   | Description                               |
| ----------- | ------ | ----------------------------------------- |
| event\_name | string | The event to trigger.                     |
| response    | table  | The table passed to all method callbacks. |

#### Returns

| Type    | Description                                                 |
| ------- | ----------------------------------------------------------- |
| boolean | `false` if any callback returned `false`, otherwise `true`. |

#### Example

```lua
local success = METHODS.trigger("custom_event", { player = source })
if not success then return end
```


# Player

Provides utility functions for retrieving player information, directional logic, entity targeting, animations, and more.

***

## Accessing the Module

```lua
local PLAYER <const> = exports.boii_utils:get("modules.player")
```

***

## Shared

### get\_cardinal\_direction(player\_ped)

Returns the cardinal direction the player is facing.

#### Parameters

| Name        | Type     | Description                                                    |
| ----------- | -------- | -------------------------------------------------------------- |
| player\_ped | `number` | The player ped (use `PlayerPedId()` or `GetPlayerPed(source)`) |

#### Example

```lua
local dir = PLAYER.get_cardinal_direction(PlayerPedId())
```

***

### get\_distance\_to\_entity(player, entity)

Calculates the distance between a player and another entity.

#### Parameters

| Name   | Type     | Description                   |
| ------ | -------- | ----------------------------- |
| player | `number` | The player entity             |
| entity | `number` | The target entity (or net ID) |

#### Example

```lua
local dist = PLAYER.get_distance_to_entity(PlayerPedId(), entity)
```

***

## Client

### get\_street\_name(player\_ped)

Gets the current street and area the player is in.

#### Parameters

| Name        | Type     | Description    |
| ----------- | -------- | -------------- |
| player\_ped | `number` | The player ped |

#### Example

```lua
local location = PLAYER.get_street_name(PlayerPedId())
```

***

### get\_region(player\_ped)

Returns the name of the region the player is located in.

#### Parameters

| Name        | Type     | Description    |
| ----------- | -------- | -------------- |
| player\_ped | `number` | The player ped |

#### Example

```lua
local region = PLAYER.get_region(PlayerPedId())
```

***

### get\_player\_details(player\_ped)

Returns a table with extended player stats and data.

#### Parameters

| Name        | Type     | Description    |
| ----------- | -------- | -------------- |
| player\_ped | `number` | The player ped |

#### Example

```lua
local details = PLAYER.get_player_details(PlayerPedId())
```

***

### get\_target\_entity(player\_ped)

Returns the entity a player is currently aiming at.

#### Parameters

| Name        | Type     | Description    |
| ----------- | -------- | -------------- |
| player\_ped | `number` | The player ped |

#### Example

```lua
local target = PLAYER.get_target_entity(PlayerPedId())
```

***

### play\_animation(player\_ped, options, callback)

Plays an animation with optional props and visual progress.

#### Parameters

| Name        | Type       | Description                     |
| ----------- | ---------- | ------------------------------- |
| player\_ped | `number`   | The player ped                  |
| options     | `table`    | Animation and prop options      |
| callback    | `function` | Function called after animation |

#### Example

```lua
PLAYER.play_animation(PlayerPedId(), {
    dict = 'anim@heists@ornate_bank@grab_cash',
    anim = 'grab',
    flags = 49,
    duration = 3000,
    freeze = true,
    props = {
        {
            model = 'prop_cs_burger_01',
            bone = 57005,
            coords = vector3(0.1, 0.0, 0.0),
            rotation = vector3(0.0, 0.0, 0.0),
            is_ped = true
        }
    }
}, function()
    print('Animation completed.')
end)
```


# Requests

Lightweight utility wrappers for native CFX request functions.

***

## Accessing the Module

```lua
local REQUESTS <const> = exports.boii_utils:get("modules.requests")
```

***

## Client

### model(model)

Requests and loads a model.

#### Parameters

| Name  | Type   | Description               |
| ----- | ------ | ------------------------- |
| model | `hash` | Hash of the model to load |

#### Example

```lua
REQUESTS.model(GetHashKey("prop_bench_01a"))
```

***

### interior(interior)

Requests and loads an interior.

#### Parameters

| Name     | Type     | Description         |
| -------- | -------- | ------------------- |
| interior | `number` | Interior ID to load |

#### Example

```lua
REQUESTS.interior(GetInteriorAtCoords(435.5, -979.0, 30.0))
```

***

### texture(texture, wait)

Requests and optionally waits for a texture dictionary to load.

#### Parameters

| Name    | Type      | Description                  |
| ------- | --------- | ---------------------------- |
| texture | `string`  | Texture dictionary name      |
| wait    | `boolean` | Whether to wait for the load |

#### Example

```lua
REQUESTS.texture("commonmenu", true)
```

***

### collision(x, y, z)

Requests collision around a coordinate.

#### Parameters

| Name | Type     | Description  |
| ---- | -------- | ------------ |
| x    | `number` | X coordinate |
| y    | `number` | Y coordinate |
| z    | `number` | Z coordinate |

#### Example

```lua
REQUESTS.collision(200.0, -1000.0, 30.0)
```

***

### anim(dict)

Requests and loads an animation dictionary.

#### Parameters

| Name | Type     | Description               |
| ---- | -------- | ------------------------- |
| dict | `string` | Animation dictionary name |

#### Example

```lua
REQUESTS.anim("amb@world_human_bum_freeway@male@base")
```

***

### anim\_set(set)

Requests and loads an animation set.

#### Parameters

| Name | Type     | Description        |
| ---- | -------- | ------------------ |
| set  | `string` | Animation set name |

#### Example

```lua
REQUESTS.anim_set("move_m@business@a")
```

***

### clip\_set(clip)

Requests and loads an animation clip set.

#### Parameters

| Name | Type     | Description   |
| ---- | -------- | ------------- |
| clip | `string` | Clip set name |

#### Example

```lua
REQUESTS.clip_set("move_clipset@pistol")
```

***

### audio\_bank(audio)

Requests and loads a script audio bank.

#### Parameters

| Name  | Type     | Description     |
| ----- | -------- | --------------- |
| audio | `string` | Audio bank name |

#### Example

```lua
REQUESTS.audio_bank("DLC_HEIST_HACKING_SNAKE_SOUNDS")
```

***

### scaleform\_movie(scaleform)

Requests and loads a scaleform movie.

#### Parameters

| Name      | Type     | Description           |
| --------- | -------- | --------------------- |
| scaleform | `string` | Name of the scaleform |

#### Returns

| Type     | Description             |
| -------- | ----------------------- |
| `number` | Handle to the scaleform |

#### Example

```lua
local handle = REQUESTS.scaleform_movie("instructional_buttons")
```

***

### cutscene(scene)

Requests and loads a cutscene.

#### Parameters

| Name  | Type     | Description   |
| ----- | -------- | ------------- |
| scene | `string` | Cutscene name |

#### Example

```lua
REQUESTS.cutscene("mp_introduction")
```

***

### ipl(str)

Requests and loads an IPL (map file).

#### Parameters

| Name | Type     | Description      |
| ---- | -------- | ---------------- |
| str  | `string` | IPL name to load |

#### Example

```lua
REQUESTS.ipl("hei_bi_hw1_13_door")
```


# Strings

Provides utility functions for common string operations including casing, trimming, splitting, and generating random strings.

***

## Accessing the Module

```lua
local STRINGS <const> = exports.boii_utils:get("modules.strings")
```

***

## Shared

### capitalize(str)

Capitalizes the first letter of each word in the string.

#### Parameters

| Name | Type     | Description              |
| ---- | -------- | ------------------------ |
| str  | `string` | The string to capitalize |

#### Example

```lua
local result = STRINGS.capitalize("hello world") -- "Hello World"
```

***

### random\_string(length)

Generates a random alphanumeric string of a given length.

#### Parameters

| Name   | Type     | Description                      |
| ------ | -------- | -------------------------------- |
| length | `number` | The desired length of the string |

#### Example

```lua
local id = STRINGS.random_string(8) -- e.g., "aZ82xY0p"
```

***

### split(str, delimiter)

Splits a string into parts using a given delimiter.

#### Parameters

| Name      | Type     | Description               |
| --------- | -------- | ------------------------- |
| str       | `string` | The string to split       |
| delimiter | `string` | The delimiter to split on |

#### Example

```lua
local parts = STRINGS.split("one,two,three", ",") -- parts = {"one", "two", "three"}
```

***

### trim(str)

Trims whitespace from both ends of the string.

#### Parameters

| Name | Type     | Description        |
| ---- | -------- | ------------------ |
| str  | `string` | The string to trim |

#### Example

```lua
local clean = STRINGS.trim("  hello world  ") -- "hello world"
```


# Tables

Provides utility functions for working with tables, including printing, copying, comparison, and value checking.

***

## Accessing the Module

```lua
local TABLES <const> = exports.boii_utils:get("modules.tables")
```

***

## Shared

### print\_table(t, indent)

Recursively prints the contents of a table to the console. Useful for debugging nested data.

#### Parameters

| Name   | Type     | Description                        |
| ------ | -------- | ---------------------------------- |
| t      | `table`  | The table to print                 |
| indent | `string` | (Optional) Indentation for nesting |

#### Example

```lua
TABLES.print({ hello = "world", nested = { foo = true } })
```

***

### table\_contains(tbl, val)

Checks if a table contains a specific value.

#### Parameters

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| tbl  | `table` | Table to search in |
| val  | `any`   | Value to look for  |

#### Example

```lua
local found = TABLES.contains({1, 2, {3}}, 3) -- true
```

***

### deep\_copy(t)

Creates a deep copy of the given table.

#### Parameters

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| t    | `table` | Table to deep copy |

#### Example

```lua
local original = { a = 1, nested = { b = 2 } }
local copy = TABLES.deep_copy(original)
```

***

### deep\_compare(t1, t2)

Compares two tables deeply.

#### Parameters

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| t1   | `table` | First table  |
| t2   | `table` | Second table |

#### Example

```lua
local is_same = TABLES.compare({ a = { b = 1 } }, { a = { b = 1 } }) -- true
```

***

### Notes

* These functions work on both flat and nested tables.
* `deep_copy` ensures references are not shared between the original and copied table.
* `deep_compare` checks keys and values recursively.


# Timestamps

Provides utility functions for working with date, time, and UNIX timestamps.

***

## Accessing the Module

```lua
local TIMESTAMPS <const> = exports.boii_utils:get("modules.timestamps")
```

***

## Server

### get\_timestamp()

Gets the current UNIX timestamp and its formatted string.

#### Returns

| Name      | Type   | Description                |
| --------- | ------ | -------------------------- |
| timestamp | number | Current UNIX timestamp     |
| formatted | string | Formatted date-time string |

#### Example

```lua
local time = TIMESTAMPS.get_timestamp()
print(time.timestamp)  -- 1700000000
print(time.formatted)  -- "2025-03-29 18:00:00"
```

***

### convert\_timestamp(timestamp)

Converts a UNIX timestamp to a readable date and time.

#### Parameters

| Name      | Type   | Description               |
| --------- | ------ | ------------------------- |
| timestamp | number | UNIX timestamp to convert |

#### Returns

| Name | Type   | Description          |
| ---- | ------ | -------------------- |
| date | string | Date in `YYYY-MM-DD` |
| time | string | Time in `HH:MM:SS`   |
| both | string | Full datetime string |

#### Example

```lua
local result = TIMESTAMPS.convert_timestamp(os.time())
print(result.both) -- "2025-03-29 18:00:00"
```

***

### get\_current\_date\_time()

Gets the current full date and time information.

#### Returns

| Name      | Type   | Description           |
| --------- | ------ | --------------------- |
| timestamp | number | UNIX timestamp        |
| date      | string | `YYYY-MM-DD`          |
| time      | string | `HH:MM:SS`            |
| both      | string | Full formatted string |

#### Example

```lua
local now = TIMESTAMPS.get_current_date_time()
print(now.date, now.time)
```

***

### add\_days\_to\_date(date, days)

Adds days to a given date.

#### Parameters

| Name | Type   | Description               |
| ---- | ------ | ------------------------- |
| date | string | Base date in `YYYY-MM-DD` |
| days | number | Number of days to add     |

#### Returns

| Type   | Description              |
| ------ | ------------------------ |
| string | New date in `YYYY-MM-DD` |

#### Example

```lua
local new_date = TIMESTAMPS.add_days_to_date("2025-03-29", 7)
print(new_date) -- "2025-04-05"
```

***

### date\_difference(start\_date, end\_date)

Calculates the number of days between two dates.

#### Parameters

| Name        | Type   | Description             |
| ----------- | ------ | ----------------------- |
| start\_date | string | Start date `YYYY-MM-DD` |
| end\_date   | string | End date `YYYY-MM-DD`   |

#### Returns

| Name | Type   | Description                 |
| ---- | ------ | --------------------------- |
| days | number | Absolute difference in days |

#### Example

```lua
local diff = TIMESTAMPS.date_difference("2025-03-01", "2025-03-29")
print(diff.days) -- 28
```

***

### convert\_timestamp\_ms(timestamp\_ms)

Converts a millisecond-based UNIX timestamp to human-readable format.

#### Parameters

| Name          | Type   | Description                    |
| ------------- | ------ | ------------------------------ |
| timestamp\_ms | number | UNIX timestamp in milliseconds |

#### Returns

| Name | Type   | Description           |
| ---- | ------ | --------------------- |
| date | string | `YYYY-MM-DD`          |
| time | string | `HH:MM:SS`            |
| both | string | Full formatted string |

#### Example

```lua
local readable = TIMESTAMPS.convert_timestamp_ms(1700000000000)
print(readable.both) -- "2025-03-29 18:00:00"
```


# UI Bridges

Provides a unified API for handling DrawText UI and Notifications across multiple FiveM resources. Automatically routes to the appropriate underlying implementation based on what's active.

***

### Accessing the Module

```lua
local DRAWTEXT <const> = exports.boii_utils:get("modules.drawtext")
local NOTIFY <const> = exports.boii_utils:get("modules.notifications")
```

***

## Server

### drawtext.show(source, options)

Displays drawtext UI for a specific client.

#### Parameters

| Name      | Type     | Description                       |
| --------- | -------- | --------------------------------- |
| source    | `number` | Player server ID                  |
| options   | `table`  | Drawtext options                  |
| └ message | `string` | Text message to display           |
| └ icon    | `string` | Optional icon (depends on system) |

#### Example

```lua
DRAWTEXT.show(1, { message = "Hello, player!", icon = "info" })
```

***

### drawtext.hide(source)

Hides drawtext UI for a specific client.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player server ID |

#### Example

```lua
DRAWTEXT.hide(1)
```

***

### notifications.send(source, options)

Sends a notification to a specific client.

#### Parameters

| Name       | Type     | Description                    |
| ---------- | -------- | ------------------------------ |
| source     | `number` | Player server ID               |
| options    | `table`  | Notification options           |
| └ type     | `string` | Notification type (e.g., info) |
| └ message  | `string` | Notification message           |
| └ duration | `number` | Duration in ms                 |

#### Example

```lua
NOTIFY.send(1, { type = "info", message = "You received $500", duration = 5000 })
```

***

## Client

### drawtext.show(options)

Displays drawtext UI locally on the client.

#### Parameters

| Name      | Type     | Description             |
| --------- | -------- | ----------------------- |
| options   | `table`  | Drawtext options        |
| └ message | `string` | Text message to display |
| └ icon    | `string` | Optional icon           |

#### Example

```lua
DRAWTEXT.show({ message = "Press E to interact", icon = "info" })
```

***

### drawtext.hide()

Hides the drawtext UI locally on the client.

#### Example

```lua
DRAWTEXT.hide()
```

***

### notifications.send(options)

Displays a notification on the client.

#### Parameters

| Name       | Type     | Description                       |
| ---------- | -------- | --------------------------------- |
| options    | `table`  | Notification options              |
| └ type     | `string` | Notification type (e.g., success) |
| └ message  | `string` | Notification message              |
| └ duration | `number` | Duration in ms                    |

#### Example

```lua
NOTIFY.send({ type = "success", message = "Mission Complete!", duration = 3000 })
```


# Vehicles

Provides functions for querying and modifying vehicle state, condition, customization, and spawning. Primarily client-side.

***

## Accessing the Module

```lua
local VEHICLES <const> = exports.boii_utils:get("modules.vehicles")
```

***

## Client

### get\_vehicle\_plate(vehicle)

Returns the license plate of a vehicle.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local plate = VEHICLES.get_vehicle_plate(vehicle)
```

***

### get\_vehicle\_model(vehicle)

Returns the lowercase model name of a vehicle.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local model = VEHICLES.get_vehicle_model(vehicle)
```

***

### get\_doors\_broken(vehicle)

Returns a table showing which doors are damaged.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local doors = VEHICLES.get_doors_broken(vehicle)
```

***

### get\_windows\_broken(vehicle)

Returns a table showing which windows are broken.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local windows = VEHICLES.get_windows_broken(vehicle)
```

***

### get\_tyre\_burst(vehicle)

Returns a table showing burst tyres.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local tyres = VEHICLES.get_tyre_burst(vehicle)
```

***

### get\_vehicle\_extras(vehicle)

Returns a table of toggled vehicle extras.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local extras = VEHICLES.get_vehicle_extras(vehicle)
```

***

### get\_custom\_xenon\_color(vehicle)

Returns a custom xenon color as `{r, g, b}`.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local color = VEHICLES.get_custom_xenon_color(vehicle)
```

***

### get\_vehicle\_mod(vehicle, mod\_type)

Returns index and variation of a vehicle mod.

#### Parameters

| Name      | Type     | Description              |
| --------- | -------- | ------------------------ |
| vehicle   | `number` | Vehicle entity handle    |
| mod\_type | `number` | Mod slot index (0 to 49) |

#### Example

```lua
local mod = VEHICLES.get_vehicle_mod(vehicle, 15)
```

***

### get\_vehicle\_properties(vehicle)

Returns a full set of mod and condition data.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local props = VEHICLES.get_vehicle_properties(vehicle)
```

***

### get\_vehicle\_mods\_and\_maintenance(vehicle)

Returns mod and maintenance data separately.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local mods, maintenance = VEHICLES.get_vehicle_mods_and_maintenance(vehicle)
```

***

### get\_vehicle\_class(vehicle)

Returns the class name (e.g. "sports") of the vehicle.

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local class = VEHICLES.get_vehicle_class(vehicle)
```

***

### get\_vehicle\_class\_details(vehicle)

Returns stats for the vehicle class (traction, speed, etc).

#### Parameters

| Name    | Type     | Description           |
| ------- | -------- | --------------------- |
| vehicle | `number` | Vehicle entity handle |

#### Example

```lua
local stats = VEHICLES.get_vehicle_class_details(vehicle)
```

***

### get\_vehicle\_details(use\_current\_vehicle)

Returns full detailed data for a vehicle.

#### Parameters

| Name                  | Type      | Description                           |
| --------------------- | --------- | ------------------------------------- |
| use\_current\_vehicle | `boolean` | Use the current vehicle or nearby one |

#### Example

```lua
local info = VEHICLES.get_vehicle_details(true)
```

***

### spawn\_vehicle(vehicle\_data)

Spawns and configures a vehicle.

#### Parameters

| Name          | Type    | Description                                |
| ------------- | ------- | ------------------------------------------ |
| vehicle\_data | `table` | Vehicle data including model, coords, mods |

#### Example

```lua
local vehicle = VEHICLES.spawn_vehicle({
  model = 'sultan',
  coords = vector4(0.0, 0.0, 72.0, 90.0),
  mods = { max_performance = true }
})
```


# Version

Provides utilities for inspecting, modifying, and spawning vehicles with customization support.

***

## Accessing the Module

```lua
local VEHICLES <const> = exports.boii_utils:get("modules.vehicles")
```

***

## Server

### check\_version(opts)

Checks the version of a resource against the latest version on GitHub.

#### Parameters

| Name             | Type       | Description                                                                     |
| ---------------- | ---------- | ------------------------------------------------------------------------------- |
| opts             | `table`    | Configuration table containing:                                                 |
| └ resource\_name | `string`   | The resource name (optional, defaults to current resource)                      |
| └ url\_path      | `string`   | Path to the versions JSON file on GitHub                                        |
| └ callback       | `function` | Callback function receiving `(success, current_version, latest_version, notes)` |

#### Example

```lua
local options = {
    resource_name = 'my_resource',
    url_path = 'myuser/myrepo/refs/heads/main/versions.json',
    callback = function(success, current, latest, notes)
        if success then
            print('Up to date!')
        else
            print('Outdated:', notes)
        end
    end
}

VERSION.check(options)
```


# XP

Handles player XP tracking, leveling, and data persistence for skills, reputation, and other growth systems.

***

## Accessing the Module

```lua
local XP <const> = exports.boii_utils:get("modules.xp")
```

***

## Server

### get\_all\_xp(source)

Retrieves all XP data for a player.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |

#### Returns

| Type  | Description         |
| ----- | ------------------- |
| table | Table of XP entries |

#### Example

```lua
local all_xp = XP.get_all(source)
```

***

### get\_xp(source, xp\_id)

Gets a specific XP entry for a player.

#### Parameters

| Name   | Type     | Description      |
| ------ | -------- | ---------------- |
| source | `number` | Player source ID |
| xp\_id | `string` | The XP ID        |

#### Returns

| Type  | Description        |
| ----- | ------------------ |
| table | XP data for the ID |

#### Example

```lua
local fishing = XP.get(source, "fishing")
```

***

### set\_xp(source, xp\_id, amount)

Sets a player's XP to a fixed value.

#### Parameters

| Name   | Type     | Description            |
| ------ | -------- | ---------------------- |
| source | `number` | Player source ID       |
| xp\_id | `string` | The XP ID              |
| amount | `number` | Amount of XP to assign |

#### Example

```lua
XP.set(source, "fishing", 100)
```

***

### add\_xp(source, xp\_id, amount)

Adds XP to a player's skill and handles level-ups.

#### Parameters

| Name   | Type     | Description         |
| ------ | -------- | ------------------- |
| source | `number` | Player source ID    |
| xp\_id | `string` | The XP ID           |
| amount | `number` | Amount of XP to add |

#### Example

```lua
XP.add(source, "fishing", 10)
```

***

### remove\_xp(source, xp\_id, amount)

Removes XP from a player's skill and handles level-downs.

#### Parameters

| Name   | Type     | Description              |
| ------ | -------- | ------------------------ |
| source | `number` | Player source ID         |
| xp\_id | `string` | The XP ID                |
| amount | `number` | Amount of XP to subtract |

#### Example

```lua
XP.remove(source, "fishing", 5)
```

***

## Client

### get\_all\_xp()

Requests all XP data for the local player.

#### Returns

| Type  | Description        |
| ----- | ------------------ |
| table | XP data for player |

#### Example

```lua
local xp_data = XP.get_all()
```




---

[Next Page](/llms-full.txt/1)

