# pc\_collections

Let your players exchange sets of items for rewards and xp.

## Video preview:

{% embed url="<https://youtu.be/U6JvJbEpvAg>" %}
Script showcase
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Overview of pc\_collections features

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FPS77S6hR9BTW2JswfRQP%2Fmenu.png?alt=media&amp;token=c4b83b8c-39c9-4003-b578-c0916b1a67ad" alt=""><figcaption><p><em>Collections menu</em></p></figcaption></figure>

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

Quick installation guide for this resource

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however to get the most out of it, basic knowledge of lua and scripts configuration is very useful.
{% endhint %}

### Dependencies & integrations

This script interacts with some other resources:

|                                                                                                                                                                                          Resource                                                                                                                                                                                         |                                                       Recommended version                                                      |    Required    |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------: | :------------: |
| <p><strong>One</strong> of supported frameworks:<br>• ESX: <a href="https://github.com/esx-framework/esx_core">es\_extended</a><br>• QBCore: <a href="https://github.com/qbcore-framework/qb-core/tree/main">qb-core</a><br>• QBox: <a href="https://github.com/Qbox-project/qbx_core">qbx\_core</a><br>• OX: <a href="https://github.com/overextended/ox_core/releases">ox\_core</a></p> | <p>• ESX: 1.6.0+<br>• QBCore: 1.2.6+<br>• QBox: 1.22.3+<br>• <a data-footnote-ref href="#user-content-fn-1">OX: 1.2.2+</a></p> |       Yes      |
|                                                                                                                                                                     [oxmysql](https://github.com/overextended/oxmysql)                                                                                                                                                                    |                                                             2.12.0+                                                            |    No\*[^2]    |
|                                                                                                                                                           [ox\_inventory](https://github.com/overextended/ox_inventory/releases)                                                                                                                                                          |                                                             2.38.1+                                                            |   No\*\*[^3]   |
|                                                                                                                                                                 [pc\_skills](https://tebex.poisoncode.com/scripts/skills)                                                                                                                                                                 |                                                               1.1                                                              |  No\*\*\*[^4]  |
|                                                                                                                                                                 [pc\_notifications](https://tebex.poisoncode.com/scripts/)                                                                                                                                                                |                                                               1.0                                                              | No\*\*\*\*[^5] |

*\*- Limiting how many times players can claim given collection is still possible without oxmysql, data will be saved in a .json file on server.*

*\*\*- If you have ox\_inventory, item icons will be downloaded automatically, if not you'll have to add them manually, see* [*tutorials*](/scripts/pc_collections/tutorials).

*\*\*\*- Using pc\_skills is recommended for the best functionality, it gives an option to lock some collections until player reches given level, and awarding xp for claiming collections.*

*\*\*\*\*- pc\_notifications is integrated by default. You can use any notifications system, but you will have to add it in utils*\
\
*"<mark style="color:green;">Recommended version</mark>" only tells you what version of the resource has been tested, other versions (including older ones) will likely work as well.*

### Installing the resource

{% stepper %}
{% step %}

### Add the resource to your server

Upload the file into your resources folder

Load `database.sql` into your database <mark style="color:blue;">(Optional)</mark>

Add the script by adding following to the server.cfg: *`ensure pc_collections`*
{% endstep %}

{% step %}

### Restart your server

Restart the server for the change to take effect and check for any errors
{% endstep %}
{% endstepper %}

[^1]: If you want to use ox\_core you need to uncomment this line in fxmanifest.lua

    ```lua
    '@ox_core/lib/init.lua',
    ```

[^2]: *Limiting how many times players can claim given collection is still possible without oxmysql, data will be saved in a .json file on server.*

[^3]: *If you have ox\_inventory, item icons will be downloaded automatically, if not you'll have to add them manually, see* [*tutorials*](/scripts/pc_collections/tutorials).

[^4]: *\*\*\*- Using pc\_skills is recommended for the best functionality, it gives an option to lock some collections until player reches given level, and awarding xp for claiming collections.*

[^5]: pc\_notifications is integrated by default. You can use any notifications system, but you will have to add it in utils


# Exports & events

Necessary exports and events available in this resource

## Client side

<details>

<summary><strong>Opening the collections menu</strong></summary>

```lua
TriggerEvent('pc_collections:client:openCollections')
```

```lua
exports.pc_collections:openCollections()
```

Collections menu will open for player that triggers the export/event. There's also /collections command

</details>

## Server side

<details>

<summary><strong>Opening the collections menu for given player</strong></summary>

```lua
TriggerEvent('pc_skills:server:openCollections', player)
```

```lua
exports.pc_collections:openCollections(player)
```

• player - playerId -  id of the player you want to open menu for

Collections menu will open for player with given id.

</details>


# Open files preview

Config files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is customizable, there is a total of 2 files accessible for you.
{% endhint %}

## config/config.lua

<details>

<summary>Click here to see a preview config.lua</summary>

```lua
Config = {}
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://tebex.poisoncode.com

Config.Debug = false -- Enable debug messages
Config.Framework = "ESX" -- Available options: "ESX", "QBCore", "QBox", "ox_core"

Config.UseOxInventory = GetResourceState('ox_inventory') == 'started' or false
Config.UsePcNotifications = GetResourceState('pc_notifications') == 'started' or false

Config.pc_skills =  {
    enabled = false, -- Set to true if you want to use pc_skills integration. pc_skills resource is required https://tebex.poisoncode.com/product/6598558
    skill = "skill1", -- Skill name from pc_skills
}

Config.ShowClaimLimits = true -- Show claim limits in the UI

Config.Collections = {
    ["collection1"] = { -- Collection name
        order = 1, -- Order in the list
        label = "Food collection", -- Collection name
        limit = false, -- Limit how many times player can complete this collection, set to false for unlimited
        items = { -- Items required to complete this collection
            {name = "burger", label = "Burger", count = 2},
            {name = "hotdog", label = "Hotdog", count = 3},
            {name = "coffe", label = "Coffe", count = 1},
        },
        reward = { -- Items rewarded for completing this collection
            {name = "money", label = "Cash", count = 500},
            {name = "water", label = "Water Bottles", count = 3},
        },

        -- pc_skills integration
        lvl = 1, -- Level required to unlock this collection, only works if Config.pc_skills.enabled is set to true
        rewardXP = 200, -- Experience points rewarded for completing this collection, only works if Config.pc_skills.enabled is set to true
    },
    ["collection2"] = {
        order = 2,
        label = "Drugs collection",
        limit = 1,
        items = {
            {name = "weed", label = "Weed", count = 1},
            {name = "meth_60", label = "Meth 60%", count = 1},
            {name = "coke_bag30", label = "Coke 30%", count = 1},
        },
        reward = {
            {name = "parts_premium", label = "Premium Parts", count = 1},
            {name = "weapon_pistol", label = "pistol", count = 1}
        },

        -- pc_skills integration
        lvl = 1,
        rewardXP = 10,
    },
    ["collection3"] = {
        order = 3,
        label = "Advanced collection",
        limit = 1,
        items = {
            {name = "phone", label = "Phone", count = 1},
            {name = "lowbulletproofvest", label = "Bulletproof vest", count = 1},
            {name = "mask", label = "Mask", count = 1},
        },
        reward = {
            {name = "rare_parts_premium", label = "Rare Premium Parts", count = 2},
            {name = "gold_bar", label = "Gold Bars", count = 3}
        },

        -- pc_skills integration
        lvl = 10,
        rewardXP = 20,
    },
}
```

</details>

## config/translations.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://tebex.poisoncode.com

Translations = {
    collections = "Collections",
    rewards = "Rewards",
    claim = "Claim",
    limitReached = "Limit reached",
    claimLimit = "Claim Limit",
    unlimitedClaims = "Unlimited Claims",
    exchange_successful = "Exchange successful",
    exchange_successful_desc = "You have successfully exchanged your items",
    limit = "Limit",
    unlimited = "Unlimited",
}
```

</details>

## client/utils.lua

<details>

<summary>Click here to see a preview utils.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://tebex.poisoncode.com

RegisterCommand("collections", function(source, args)
    showUI(not isUiDisplayed)
end)
```

</details>

## server/utils.lua

<details>

<summary>Click here to see a preview utils.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://tebex.poisoncode.com

function sendClientNotification(source, title, text, time, type)
    if Config.UsePcNotifications then
        TriggerClientEvent('pc_notifications:Notify', source, type, title, text, time, true) 
    else

    end
end
```

</details>

{% hint style="info" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Tutorials

These tutorials will walk you through all the options available.

<details>

<summary>Using icons without ox_inventory</summary>

When using ox\_invenotry, the icons are set automatically. However if your server doesn't use ox\_inventory you'll have to upload the icons manually. Here's how to do it:

1. Prepare your images (in a .png format)
2. Open `pc_collections/html/img`
3. Drop your images into this folder
4. Use `refresh` and `restart pc_collections` in your server console
5. Verify that icons show up in the UI

Youtube tutorial: (soon!)

</details>


# Updates

Page dedicated to update trailers and hisotry.

### No updates yet


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


# Version compatibility

Page with information helpful for converting your config files to a new version


# pc\_organizations

The most complete crime organizations system including advanced tablet, zone capturing and drug wars.

## Video preview:

{% embed url="<https://youtu.be/lclnXH4k2aw>" %}
*See this script in action*
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Overview of pc\_organizations features

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2Fz86VYbpo1pTVoZtHu0MW%2Ftablet.png?alt=media&amp;token=d8a2dadd-3791-45fd-aaa7-8d43f42c9d0a" alt=""><figcaption><p><em>Organizations tablet main page</em></p></figcaption></figure>

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FidN8qYomSe6M5gNQJsFH%2Ftablet2.png?alt=media&amp;token=6d574d00-f9f9-44f3-89c2-0efea9e7812a" alt=""><figcaption><p><em>Organizations tablet upgrades page</em></p></figcaption></figure>

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2F0IWEwsIrNImlTQrbtqAP%2Fzone_cap.png?alt=media&amp;token=525256cf-71af-48a0-8a95-4d8ea82fab44" alt=""><figcaption><p><em>Capturing a zone</em></p></figcaption></figure>

***

<div><figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2Fu2PhYpP58hLMftHmoeQm%2Fweb_prv.png?alt=media&amp;token=bee00dd7-d12f-4656-b6d9-db9962753f3b" alt="" width="563"><figcaption><p><em>Organization private webhook</em></p></figcaption></figure> <figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FwRcVBASFVyPFi2sHfTJu%2Fweb_adm.png?alt=media&amp;token=7134236a-3ca7-43b3-8849-3d239f5eb1eb" alt="" width="563"><figcaption><p><em>Admin webhook</em></p></figcaption></figure></div>

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

Quick installation guide for pc\_organizations

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however to get the most out of it, basic knowledge of lua and scripts configuration is very useful.
{% endhint %}

### Dependencies & integrations

This script interacts with some other resources:

<table><thead><tr><th align="center">Resource</th><th width="227" align="center">Recommended version</th><th align="center">Required</th></tr></thead><tbody><tr><td align="center">Framework:<br>• ESX: <a href="https://github.com/esx-framework/esx_core">es_extended</a></td><td align="center">es_extended: 1.6.0 +</td><td align="center"><strong>Yes</strong></td></tr><tr><td align="center"><a href="https://github.com/overextended/oxmysql">oxmysql</a></td><td align="center">Latest</td><td align="center"><strong>Yes</strong></td></tr><tr><td align="center"><a href="https://github.com/overextended/ox_lib">ox_lib</a></td><td align="center">3.18.0+</td><td align="center"><strong>Yes</strong></td></tr><tr><td align="center"><a href="https://github.com/overextended/ox_inventory">ox_inventory</a></td><td align="center">2.38.1+</td><td align="center"><strong>Yes</strong></td></tr><tr><td align="center"><a href="https://github.com/mkafrin/PolyZone">PolyZone</a></td><td align="center">2.6.1+</td><td align="center"><strong>Yes*</strong></td></tr><tr><td align="center"><a href="https://tebex.poisoncode.com/scripts/skills">pc_skills</a></td><td align="center">1.0+</td><td align="center">No (but recommended)**</td></tr><tr><td align="center"><a href="https://tebex.poisoncode.com/scripts/notifications">pc_notifications</a></td><td align="center">1.0+</td><td align="center">No</td></tr></tbody></table>

*"Recommended version" only tells you what version of the resource has been tested, other versions (including older ones) will likely work as well.*

\*- *PolyZone is only required if you intend on using drug wars module. See* [*tutorials*](/scripts/pc_organizations/tutorials) *if you want to remove it.*\
\*\*- *pc\_skills is a resource made by us, it's not mandatory, but if you have it, additional features will be* available *(eg. gaining xp & all it's perks, like higher sell chance).*

### Installing the resource

{% stepper %}
{% step %}

### Add the resource (& dependencies above) to your server

Upload the file into your resources folder

Load `database.sql` into your database

Add the script by adding following to the server.cfg: *`ensure pc_organizations`*
{% endstep %}

{% step %}

### Adjust settings

Navigate to `config.lua` and utils to set everything according to your needs
{% endstep %}

{% step %}

### Restart your server

Restart the server for the change to take effect and check for any errors
{% endstep %}
{% endstepper %}


# Exports & events

Exports and events available in this resource

## Client side

<details>

<summary><strong>Opening organization tablet</strong></summary>

```lua
TriggerEvent("pc_organizations:OpenTablet")
```

```lua
exports.pc_organizations:openTablet()
```

<mark style="color:blue;">Opens the organization tablet for triggering player.</mark>

</details>

<details>

<summary><strong>Checking for organization job</strong></summary>

```lua
exports.pc_organizations:hasOrgJob()
```

<mark style="color:blue;">returns:</mark>\
If player is a part of organization returns organization name (string) else returns false (boolean)

</details>

## Server side

<details>

<summary><strong>Opening organization tablet</strong></summary>

```lua
TriggerEvent("pc_organizations:OpenTablet", player)
```

```lua
exports.pc_organizations:openTablet(player)
```

• player - playerId - id of the player you want to check

<mark style="color:blue;">Opens the organization tablet for player with given id.</mark>

</details>

<details>

<summary><strong>Checking for organization job</strong></summary>

```lua
exports.pc_organizations:hasOrgJob(player)
```

• player - playerId - id of the player you want to check

<mark style="color:blue;">returns:</mark>\
If player is a part of organization returns organization name (string) else returns false (boolean)

</details>


# Open files preview

Config files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is customizable, there is a total of 4 files accessible for you.
{% endhint %}

## config/config.lua

<details>

<summary>Click here to see a preview config.lua</summary>

```lua
Config = {}
-- _____       _                  _____          _                                     _          _   _                    __   ___  
-- |  __ \    (_)                / ____|        | |                                   (_)        | | (_)                  /_ | / _ \ 
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___     ___  _ __ __ _  __ _ _ __  _ ______ _| |_ _  ___  _ __  ___    | || | | |
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \   / _ \| '__/ _` |/ _` | '_ \| |_  / _` | __| |/ _ \| '_ \/ __|   | || | | |
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/  | (_) | | | (_| | (_| | | | | |/ / (_| | |_| | (_) | | | \__ \   | || |_| |
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|   \___/|_|  \__, |\__,_|_| |_|_/___\__,_|\__|_|\___/|_| |_|___/   |_(_)___/ 
--                                                                    __/ |                                                          
--                                                                   |___/                                                                                                    
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io

--   __  __       _                          __ _       
--  |  \/  |     (_)                        / _(_)      
--  | \  / | __ _ _ _ __     ___ ___  _ __ | |_ _  __ _ 
--  | |\/| |/ _` | | '_ \   / __/ _ \| '_ \|  _| |/ _` |
--  | |  | | (_| | | | | | | (_| (_) | | | | | | | (_| |
--  |_|  |_|\__,_|_|_| |_|  \___\___/|_| |_|_| |_|\__, |
--                                                 __/ |
--                                                |___/ 

Config.clientDebug = false -- Enables additional debug messages on the client side console (F8), we recommend turning this off while not debugging
Config.serverDebug = true -- Enables additional debug messages on the server side console, you can keep it on if these messages don't bother you
-- *Error and security messages will be print anyway*

Config.integrations = {
    -- Mandatory
    ox_inventory = GetResourceState('ox_inventory') == 'started' or false,
    -- Optional
    pc_notifications = GetResourceState('pc_notifications') == 'started' or false,
    pc_skills = GetResourceState('pc_skills') == 'started' or false,
}

-- Here you can turn on or off specific modules, we recommend keeping them all on for best experience
Config.modules = {
    zones = true, -- enables zones module
    drugwars = true, -- enables drugwars module
}

-- Here you can choose which options should be available in ox_target
-- You can still control the access to these options with permissions in Config.Permissions
Config.target = {
    tablet = true, -- enables opening the tablet with ox_target
    safeCheck = true, -- enables checking the organization's safe with ox_target
    safeDeposit = true, -- enables depositing and withdrawing money from the organization's safe with ox_target
    safeWithdraw = true, -- enables withdrawing money from the organization's safe with ox_target
    warehouse = true, -- enables opening the warehouse with ox_target
    wardrobe = true, -- enables opening the wardrobe with ox_target
    wardrobeAdd = true, -- enables adding outfits to the wardrobe with ox_target
    wardrobeRemove = true, -- enables removing an outfit from the wardrobe with ox_target
}

-- Default values all organizations start with
Config.defaultValues = {
    ["membersCount"] = 7, -- example: each organization starts with max. 7 members (In slots)
    ["warehouseSlots"] = 1, -- (In slots (ox_inventory))
    ["warehouseWeight"] = 500, -- (In kg (ox_inventory))
    ["drugwarsTax"] = 10, -- (In % of sale)
}

-- Price for each upgrade
Config.upgradesPrice = {
    ["membersCount"] = 50000, -- example: 1 upgrade = 50000$ from organization's safe
    ["warehouseSlots"] = 25000,
    ["warehouseWeight"] = 15000,
    ["drugwarsTax"] = 50000,
}

-- How much each upgrade adds
Config.upgradesIncrements = {
    ["membersCount"] = 1, -- example: 1 upgrade = 1 additional member slot
    ["warehouseSlots"] = 1,
    ["warehouseWeight"] = 5,
    ["drugwarsTax"] = 1,
}

-- Max values for each upgrade
Config.upgradesMaxValues = {
    ["membersCount"] = 20, -- example: maximum 20 members in each organization
    ["warehouseSlots"] = 200,
    ["warehouseWeight"] = 500000,
    ["drugwarsTax"] = 20,
}

-- Feel free to change permission LABEL. Changing permission NAME WILL BREAK THE SCRIPT.
Config.Permissions = {
    {name = "permissionsManage", label = "managing permissions"},
    {name = "hireMembers", label = "recruiting members"},
    {name = "fireMember", label = "firing members"},
    {name = "upgrades", label = "managing upgrades"},
    {name = "noteAdd", label = "adding notes"},
    {name = "noteRemove", label = "removing notes"},
    {name = "safe", label = "safe access"},
    {name = "warehouse", label = "warehouse access"},
    {name = "wardrobeOpen", label = "wardrobe access "},
    {name = "wardrobeAdd", label = "adding outfits"},
    {name = "wardrobeRemove", label = "removing outfits"},
    {name = "webhookModify", label = "webhook changes"}
}

-- You can configure organizations here:
Config.Organizations = {
    ["families"] = {
        label = "Families",
        Blip = vec3(-157.0381, -1616.7047, 32.6994),
        Safe = {
            coords = vector3(-163.93, -1619.61, 33.85),
            size = vector3(0.6, 0.6, 0.6),
            rotation = 340.0
        },
        Storage = {
            coords = vector3(-158.96, -1608.36, 33.65),
            size = vector3(2.2, 0.6, 2.0),
            rotation = 340.0
        },
        Wardrobe = {
            coords = vector3(-162.9, -1613.6, 33.65),
            size = vector3(2.8, 0.6, 2.0),
            rotation = 340.0
        }
    },
    ["lost"] = {
        label = "Lost",
        Blip = vec3(974.3788, -119.4971, 73.3864),
        Safe = {
            coords = vector3(977.59, -104.65, 74.65),
            size = vector3(1.0, 0.8, 1.2),
            rotation = 310.0
        },
        Storage = {
            coords = vector3(987.35, -93.43, 74.85),
            size = vector3(0.8, 2.2, 2.0),
            rotation = 315.0
        },
        Wardrobe = {
            coords = vector3(972.17, -97.62, 74.87),
            size = vector3(0.4, 3.8, 2.0),
            rotation = 310.0
        }
    },
    ["marabunta"] = {
        label = "Marabunta",
        Blip = vec3(1258.1376, -1570.7284, 57.4016),
        Safe = {
            coords = vector3(1251.25, -1580.72, 58.55),
            size = vector3(0.6, 0.6, 0.6),
            rotation = 305.0
        },
        Storage = {
            coords = vector3(1254.59, -1572.07, 58.75),
            size = vector3(1, 2.6, 2.0),
            rotation = 305.0
        },
        Wardrobe = {
            coords = vector3(1254.62, -1576.37, 58.15),
            size = vector3(0.6, 1.4, 1.0),
            rotation = 305.0
        }
    },
    ["cartel"] = {
        label = "De La Cosa",
        Blip = vec3(1403.78, 1144.02, 114.33),
        Safe = {
            coords = vector3(1394.74, 1150.37, 115.33),
            size = vector3(0.8, 1.6, 2),
            rotation = 0.0
        },
        Storage = {
            coords = vector3(1403.78, 1144.02, 114.33),
            size = vector3(0.6, 3.0, 2.0),
            rotation = 0.0
        },
        Wardrobe = {
            coords = vector3(1400.4, 1139.73, 114.33),
            size = vector3(1.4, 0.6, 2.2),
            rotation = 0.0
        }
    },
    ["mafia"] = {
        label = "La Cosa Nostra",
        Blip = vec3(1096.7808, -3099.2991, -39.0000),
        Safe = {
            coords = vector3(1087.7452, -3101.2766, -38.9614),
            size = vector3(1.0, 1.6, 1),
            rotation = 310.0
        },
        Storage = {
            coords = vector3(1100.9728, -3102.2998, -39.0000),
            size = vector3(0.6, 1.4, 2.0),
            rotation = 40.0
        },
        Wardrobe = {
            coords = vector3(1104.0432, -3102.3225, -39.0000),
            size = vector3(0.8, 3.4, 2.2),
            rotation = 40.0
        }
    },
    ["sinister"] = {
        label = "The Sinister",
        Blip = vector3(568.26, -2780.87, 6.09),
        Safe = {
            coords = vector3(519.56, -2757.89, 6.64),
            size = vector3(2.6, 1, 1),
            rotation = 330.0
        },
        Storage = {
            coords = vector3(506.36, -2756.69, 3.07),
            size = vector3(4.0, 1, 2.0),
            rotation = 330.0
        },
        Wardrobe = {
            coords = vector3(568.26, -2780.87, 6.09),
            size = vector3(3.0, 1, 2.2),
            rotation = 330.0
        }
    },
}


--   ______                                        __ _       
--  |___  /                                       / _(_)      
--     / / ___  _ __   ___  ___    ___ ___  _ __ | |_ _  __ _ 
--    / / / _ \| '_ \ / _ \/ __|  / __/ _ \| '_ \|  _| |/ _` |
--   / /_| (_) | | | |  __/\__ \ | (_| (_) | | | | | | | (_| |
--  /_____\___/|_| |_|\___||___/  \___\___/|_| |_|_| |_|\__, |
--                                                       __/ |
--                                                      |___/ 


Config.zoneSettings = {
    auto = true,
    autoStart = 1200, --- (seconds) How quickly the first zone after restarting the script/server appear
    autoDelay = 1800, --- (seconds) How quickly the next zone appears (the timer starts right after the previous zone is created)

    zoneCaptureTime = 20, --- (seconds) How long it takes to capture the zone
    zoneDuration = 600, --- (seconds) How long the zone is active 
    zoneCaptureDelay = 20, --- How quickly can you capture the zone after canceling the capture (seconds)
    zonePrepareTime = 20, --- (seconds) How long it takes to prepare the zone before it appears
    zoneSize = 150.0,
    captureSize = 6.0,
}


-- Possible loot tables for zone rewards, only one table will be chosen, at random
-- When using minCount and maxCount, it will pick a random number in that range
-- When using count, it will always use that number
Config.zoneRewards = {
    { 
        {item = 'meth', minCount = 3, maxCount = 20},
        {item = 'weed', minCount = 8, maxCount = 25},
    },
-- OR
    { 
        {item = 'black_money', minCount = 5000, maxCount = 25000},
    },
-- OR
    { 
        {item = 'WEAPON_MACHETE', minCount = 1, maxCount = 2},
        {item = 'WEAPON_KNIFE', minCount = 1, maxCount = 2},  
    },
-- OR
    { 
        {item = 'WEAPON_SNSPISTOL', count = 1},
    }
}


Config.zoneLocations = {
    {
        coords = vector3(-2527.5723, 2699.8335, -22.838), -- Center of the zone
        propCoords = { -- Crate location, only one of the values below will be choosen (at random).
            vector3(-2527.5723, 2699.8335, 1.838), 
            vector3(-2522.7871, 2733.1943, 1.8316),
            vector3(-2475.4683, 2731.6782, 1.8716),    
        }
    },

    {
        coords = vector3(383.3146, 2893.9709, -20.5480), -- Center of the zone
        propCoords = { -- Crate location, only one of the values below will be choosen (at random).
            vector3(398.6463, 2980.2148, 39.9499), 
            vector3(268.5528, 2867.1343, 73.1748),
            vector3(319.3150, 2884.2703, 48.0229),      
        }
    },

    {
        coords = vector3(1075.9691, 2366.7412, -18.2522), -- Center of the zone
        propCoords = { -- Crate location, only one of the values below will be choosen (at random).
            vector3(1089.4799, 2309.3499, 44.5132), 
            vector3(1006.8578, 2360.2310, 54.5715),
            vector3(1026.1266, 2440.3679, 43.9705),  
        }
    },

    {
        coords = vector3(1500.9552, 3549.1238, 35.6549), -- Center of the zone
        propCoords = { -- Crate location, only one of the values below will be choosen (at random).
            vector3(1516.6475, 3577.0632, 41.8550), 
            vector3(1559.3889, 3522.0605, 35.0679),
            vector3(1594.8564, 3586.1721, 38.0666),  
        }
    },

    {
        coords = vector3(978.2614, -3136.7349, -20.9008), -- Center of the zone
        propCoords = { -- Crate location, only one of the values below will be choosen (at random).
            vector3(930.9222, -3097.9087, 16.1479), 
            vector3(1029.9514, -3131.3235, 4.9008),
            vector3(959.0121, -3097.4951, 4.9008),  
        }
    }
}


-- _____                                                               __ _       
-- |  __ \                                                             / _(_)      
-- | |  | |_ __ _   _  __ _  __      ____ _ _ __ ___    ___ ___  _ __ | |_ _  __ _ 
-- | |  | | '__| | | |/ _` | \ \ /\ / / _` | '__/ __|  / __/ _ \| '_ \|  _| |/ _` |
-- | |__| | |  | |_| | (_| |  \ V  V / (_| | |  \__ \ | (_| (_) | | | | | | | (_| |
-- |_____/|_|   \__,_|\__, |   \_/\_/ \__,_|_|  |___/  \___\___/|_| |_|_| |_|\__, |
--                     __/ |                                                  __/ |
--                    |___/                                                  |___/ 
Config.warsSettings = {
    auto = true, -- should the war be started automatically
    autoStart = 1200, --- (seconds) How quickly the first war appears (after restart/start)
    autoDelay = 10800, --- (seconds) How quickly the next war appears (the timer starts right after the previous war is *created*)
    warDuration = 1800, --- (seconds) How long the war is active
}

Config.baseSellChance = 40 -- (in %) Base chance to sell drugs, without skill multiplier

-- Used only when using pc_skills resource
Config.pc_skills = {
    skillName = "skill4", -- which skill is used for drug selling
    saleXP = 10, -- how much xp you gain for each sold drug (for each piece)
    copTipoffLvl = 5, -- when player reaches this level, he will recieve tip-off if the client called cops
    lvlBonus = 1, -- how much higher is the chance to sell drugs, for each level (in %), for example each lvl = 1% added to baseSellChance
}

Config.moneyType = "black_money" -- type of money that should be given to player selling ("money" or "black_money")

Config.copsRequired = 2 -- How many cops are required to sell the drugs
Config.copsJobs = {'police', 'sheriff'} -- jobs that are counted as cops
-- example: if you have copsRequired = 2, and police and sheriff jobs are counted as cops, if there's 1 police and 1 sheriff on duty, you will be able to sell drugs

Config.blacklistedJobs = {'police', 'sheriff'} -- jobs that are blacklisted from selling drugs

-- Drugs that can be sold to npcs
-- min and max are min and max price the player will recieve
Config.Drugs = {
    ['acid'] = {min = 182, max = 444},
    ['lsd'] = {min = 145, max = 388},
    ['meth'] = {min = 113, max = 338},
    ['coke'] = {min = 125, max = 292},
    ['weed'] = {min = 148, max = 182},
}

-- Informants are only available for organization that captured a zone. When bought, they will check if there's a police officer in the zone,
-- after that they'll notify organization members when a police officer enters the zone.
Config.enableAlertNpcs = true -- Should informants be available

-- zone -- zone in which this infromant is active
-- model -- model of the npc
-- coords -- coords of the npc
-- price -- price for activating the informant
-- duration (seconds) -- how long the informant will be active
-- delay (seconds) -- delay before the informant will alert
Config.alertNpc = {
    -- zone 1
    {zone = 'zone1', model = 'a_m_m_mexlabor_01', coords = vector4(-357.7109, -640.9236, 30.7715, 207.9297), price = 2500, duration = 100, delay = 20},
    {zone = 'zone1', model = 'a_m_m_mexlabor_01', coords = vector4(-1215.3136, -569.2302, 26.3067, 163.3186), price = 5000, duration = 200, delay = 10},

    -- zone 2
    {zone = 'zone2', model = 'a_m_m_mexlabor_01', coords = vector4(963.3970, -944.5385, 41.6625, 270.3664), price = 2500, duration = 100, delay = 20},
    {zone = 'zone2', model = 'a_m_m_mexlabor_01', coords = vector4(729.7922, -992.6858, 22.9715, 3.9504), price = 5000, duration = 200, delay = 10},

    -- zone 3 
    {zone = 'zone3', model = 'a_m_m_mexlabor_01', coords = vector4(748.8958, -1692.4928, 28.3219, 167.1573), price = 2500, duration = 100, delay = 20},
    {zone = 'zone3', model = 'a_m_m_mexlabor_01', coords = vector4(433.7659, -1879.3448, 25.9994, 95.7669), price = 5000, duration = 200, delay = 10},

    -- zone 4
    {zone = 'zone4', model = 'a_m_m_mexlabor_01', coords = vector4(-394.7887, -1881.1279, 19.5278, 346.9869), price = 2500, duration = 100, delay = 20},
    {zone = 'zone4', model = 'a_m_m_mexlabor_01', coords = vector4(-532.8264, -1781.5658, 20.5106, 292.3524), price = 5000, duration = 200, delay = 10},
}

-- List of peds that can't buy drugs from the player
-- You can expand this list if you want
Config.pedBlacklist = {
    "u_f_m_casinocash_01",
    "csb_trafficwarden",
    "u_m_y_burgerdrug_01",
    "u_m_y_tattoo_01",
    "s_m_m_gardener_01",
    "s_m_y_xmech_01",
    "s_m_m_lathandy_01",
    "a_m_y_bevhills_01",
    "a_m_m_indian_01",
    "u_f_y_danceburl_01",
    "ig_andreas",
    "mp_m_shopkeep_01",
    "a_m_y_epsilon_01",
    "a_f_y_topless_01",
    "cs_paper",
    "u_m_m_jewelsec_01",
    "a_m_m_business_01",
    "a_m_y_business_02",
    "s_m_m_autoshop_01",
    "s_m_m_doctor_01",
    "mp_m_g_vagfun_01",
    "s_m_m_pilot_01",
    "a_c_cow",
    "a_c_boar",
    "a_c_cat_01",
    "a_c_chickenhawk",
    "a_c_chimp",
    "a_c_chop",
    "a_c_crow",
    "a_c_deer",
    "a_c_husky",
    "a_c_mtlion",
    "a_c_pig",
    "a_c_pigeon",
    "a_c_poodle",
    "ig_trafficwarden",
    "a_f_y_business_01",
    "cs_siemonyetarian",
    "u_m_y_chip",
    "s_f_m_fembarber",
    "mp_m_weapexp_01",
    "a_c_pug",
    "a_c_rabbit_01",
    "a_c_rat",
    "a_c_retriever",
    "a_c_rhesus",
    "a_c_rottweiler",
    "a_c_seagull",
    "a_c_shepherd",
    "a_f_y_business_02",
    "a_c_westy",
}
```

</details>

## config/translations.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io

Translations = {
    -- General
    organization = "Organization",
    balanceCheck = "Check balance",
    deposit = "Deposit cash",
    withdraw = "Withdraw cash",
    openStorage = "Open storage",
    savedOutfits = "Saved outfits",
    orgOutfits = "Organization outfits",
    addOutfit = "Add organization outfit",
    outfitName = "Outfit name",
    outfitNameDesc = "Provide a name for the outfit",
    removeOutfit = "Remove organization outfit",
    hideout = "Organization hideout",
    noOutfits = "No organization outfits saved",
    wardrobe = "Wardrobe",
    setName = "Set a name",
    outfitAdded = "Outfit added",
    outfitRemoved = "Outfit removed",
    safe = "Safe",
    depositMoney = "Deposit cash",
    depositMoneyDesc = "How much cash do you want to deposit?",
    withdrawMoney = "Withdraw cash",
    withdrawMoneyDesc = "How much cash do you want to withdraw?",
    safeMoney = "There is %s $ in the safe",
    notEnoughMoney = "You don't have enough cash",
    depositSuccess = "Cash deposited, there is now %s $ in the safe",
    notEnoughMoneySafe = "There is not enough cash in the safe",
    withdrawSuccess = "Cash withdrawn, there is now %s $ in the safe",
    notEnoughSlots = "Maximum members reached",
    maxUpgrade = "Maximum upgrade reached",
    upgradeBought = "Upgrade bought",
    noteAdded = "Note added",
    webhookModified = "Webhook modified",
    itemSwaped = "Item swaped",
    itemOut = "Item taken",
    itemIn = "Item put",
    openTablet = "Open tablet",


    -- Logs
    logOutfitAdded = "%s %s added an outfit named: %s",
    logOutfitRemoved = "%s %s removed an outfit named: %s",
    logSafeDeposit = "%s %s deposited %s $ into the safe",
    logSafeWithdraw = "%s %s withdrew %s $ from the safe",
    logMemberFired = "%s %s fired %s %s",
    logMemberHired = "%s %s hired %s %s",
    logUpgradeBought = "%s %s upgraded %s by %s",
    logNoteAdded = "%s %s added a note",
    logNoteRemoved = "%s %s removed a note",
    logWebhookModified = "%s %s modified the organization webhook",
    logItemOut = "Took",
    logItemIn = "Put",

    logZoneCreated = "Zone created",
    logZoneCreatedDesc = "A new zone has been created. \nType:%s\nLocation:%s\nBox:%s",
    logZoneReward = "Zone captured",
    logZoneRewardDesc = "A zone has been captured by %s.\nRewards: `%s`",

    logDrugsSold = "Drugs sold",
    logDrugsSoldDesc = "Player %s sold: %s x %s for %s$",

    logDrugwarsTax = "Drug sale tax",
    logDrugwarsTaxDesc = "Organization received drug sale tax of %s$",


    -- HTML (Tablet)
    permissions = "Permissions",
    save = "Save",
    cancel = "Cancel",
    noPeopleNerby = "No people nearby",
    warEndsIn = "Drug war ends in",
    warStartIn = "Drug war starts in",
    noWar = "There is no drug war",
    remove = "Remove",
    fireMember = "Fire",
    home = "Home",
    members = "Members",
    upgrades = "Upgrades",
    drugWars = "Drug wars",
    logs = "Logs",
    close = "Close",
    info = "Info",
    orgName = "name",
    finances = "Finances",
    moneyOwned = "Cash",
    notes = "Notes",
    newNote = "New note",
    noteAddTitle = "adding a note",
    confirm = "Confirm",
    hire = "Recruit",
    name = "Name",
    actions = "Actions",
    drug = "Drug",
    map = "City map",
    leaderboard = "Leaderboard",
    maxMembers = "Max members count",
    maxMembersDesc = "Increase maximum members amount",
    slot = "Slot",
    price = "Price",
    buy = "Buy",
    warehouseSlots = "Warehouse slots",
    warehouseSlotsDesc = "Increase warehouse slots amount",
    warehouseWeight = "Warehouse weight",
    warehouseWeightDesc = "Increase maximum warehouse weight",
    kg = "Kg",
    drugwarsTax = "Drugwars tax",
    drugwarsTaxDesc = "Increase the amount you get from sold drugs",
    percent = "%",
    webhook = "Webhook",
    date = "Date",
    webhookLink = "Provide a webhook link",
    membersOnline = "Members online",


    -- Zones
    zone = "Zone",
    fists = "[Fists]",
    melee = "[Melee]",
    guns = "[Guns]",
    collect = "Collect chest",
    newZoneIn = "A new zone will appear in %s seconds.",
    newZoneStart = "A new zone appeared!",
    vehicleEnter = "You can't enter the zone in a vehicle!",
    wrongWeapon = "You can't use this weapon in this zone type!",
    inVehicle = "You can't do this while in a vehicle!",
    zoneBusy = "Someone else is already capturing this zone!",
    cooldown = "You have to wait %s seconds before you can capture this zone again!",
    captureStart = "You started capturing the zone!",
    captureCancelTooFar = "Zone capture canceled. You moved too far!",
    captureCancelDamage = "Zone capture canceled. You took damage!",
    captureSuccess = "You captured the zone!",


    -- Drug wars
    trySellDrugs = "Sell drugs",
    drugDealingTitle = "Drug dealing",
    drugDealingNoItems = "You don't have any items to sell",
    drugDealingNotEnough = "You don't have enough items to sell",
    drugDealingNotEnoughCops = "Not enough cops on duty. Required at least %s",
    drugDealingFailed = "Client refused to buy the drugs",
    drugDealingFailedPolice = "Client refused to buy the drugs and called the police",
    drugDealingBlacklistedJob = "You can't sell drugs",
    drugDealingXP = "You recieved %s XP points",
    drugDealingTaxPaid = "You sold drugs in zone controlled by %s. Tax paid: %s$",

    informantBuyTitle = "Buy informant",
    informantBuyText = "Do you want to buy an informant for %s$ ? \n You will receive a message when the police enters the zone.",
    informantTitle = "Informant",
    informantWar = "There is an active war in this zone",
    infromantMoney = "You don't have enough cash",
    informantOrg = "This zone doesn't belong to your organization",
    informantActive = "There is already an active informant in this zone",
    informantBought = "Informant bought!",
    informantEnd = "Informant services ended!",
    informantPoliceEnter = "Cops entered the zone, be careful!",
    informantPolicePresent = "There are cops in the zone!",
}
```

</details>

## client/utils.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io

-------------------------------------------------------------------------------------------------------------------
--- GENERAL
-------------------------------------------------------------------------------------------------------------------

function sendNotification(title, text, time, type)
    if Config.integrations.pc_notifications then
        exports['pc_notifications']:Notify(type, title, text, time, true)
    else
        
    end
end

-------------------------------------------------------------------------------------------------------------------
--- DRUGWARS
-------------------------------------------------------------------------------------------------------------------
local alertBlips = {}
RegisterNetEvent('pc_organizations:client:drugwars.alertCops', function(location)

    sendNotification("Drug dealing", "Drug deal in progress! Location pinged on map.", 10000, "info")

    local newBlip = AddBlipForCoord(location)
    SetBlipSprite(newBlip, 3)
    SetBlipDisplay(newBlip, 4)
    SetBlipScale(newBlip, 0.9)
    SetBlipAsShortRange(newBlip, true)
    BeginTextCommandSetBlipName('STRING')
    AddTextComponentString("Drug deal in progress!") 
    EndTextCommandSetBlipName(newBlip)

    table.insert(alertBlips, newBlip)

    SetTimeout(60000, function()
        if #alertBlips > 0 then
            RemoveBlip(alertBlips[1])
            table.remove(alertBlips, 1)
        end
    end)
end)
```

</details>

## server/utils.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io
-------------------------------------------------------------------------------------------------------------------
--- MAIN
-------------------------------------------------------------------------------------------------------------------
-- Function responsible for sending client notification
function sendClientNotification(id, title, text, time, type)
    if Config.integrations.pc_notifications then
        TriggerClientEvent('pc_notifications:Notify', id, type, title, text, time, true)
    else

    end
end

-------------------------------------------------------------------------------------------------------------------
--- COMMANDS
-------------------------------------------------------------------------------------------------------------------
-- Command for adding player to an organization will all permissions
ESX.RegisterCommand('setorg', {'admin'}, function(xPlayer, args, showError)
    if args.job == "unemployed" or Config.Organizations[args.job] then
        local permissions = {}
        for i = 1, #(Config.Permissions) do
            permissions[Config.Permissions[i].name] = true
        end

        args.playerId.set("org", {name = args.job, permissions = permissions})
        TriggerClientEvent('pc_organizations:client:main.setOrg', args.playerId.source, {name = args.job, permissions = permissions})
        MySQL.prepare('UPDATE `users` SET `org` = ?, `org_permissions` = ? WHERE `identifier` = ?', {args.job, json.encode(permissions), args.playerId.identifier})
    end
end, true, {help = ('Add player to organization (with all permissions!)'), validate = true, arguments = {
    {name = 'playerId', help = ('Player ID'), type = 'player'},
    {name = 'job', help = ('Organization name or "unemployed" to remove'), type = 'string'}
}})

-- A command for resetting an organization, all members will be removed, inventory will be cleared and upgrades will be reset
ESX.RegisterCommand('resetorg', {'admin'}, function(xPlayer, args, showError)
    local orgName = args.orgName
    local xPlayers = ESX.GetExtendedPlayers()
    
    for i = 1, #(xPlayers) do
        local xPlayer = xPlayers[i]
        if xPlayer.get("org").name == orgName then
            xPlayer.set("org", {name = "unemployed", permissions = {}})
            TriggerClientEvent('pc_organizations:client:main.setOrg', xPlayer.source, {name = "unemployed", permissions = {}})
        end
    end
    
    exports.ox_inventory:ClearInventory(orgName)
    exports.ox_inventory:SetSlotCount(orgName, Config.defaultValues.warehouseSlots)
    exports.ox_inventory:SetMaxWeight(orgName, Config.defaultValues.warehouseWeight)

    Organizations[orgName] = {account = 0, members_limit = Config.defaultValues.membersCount, warehouse_slots = Config.defaultValues.warehouseSlots, warehouse_weight = Config.defaultValues.warehouseWeight, drugwars_tax = Config.defaultValues.drugwarsTax} 
    
    MySQL.prepare('UPDATE `users` SET `org` = ?, `org_permissions` = ? WHERE `org` = ?', {"unemployed", json.encode({}), orgName})
    MySQL.prepare('UPDATE `organizations` SET `account` = 0, `members_limit` = ?, `warehouse_slots` = ?, `warehouse_weight` = ?, `drugwars_tax` = ? WHERE `name` = ?', {Config.defaultValues.membersCount, Config.defaultValues.warehouseSlots, Config.defaultValues.warehouseWeight, Config.defaultValues.drugwarsTax, orgName})
    MySQL.query.await('DELETE FROM `organizations_clothes` WHERE `org` = ?', {orgName})
    MySQL.query.await('DELETE FROM `organizations_logs` WHERE `org` = ?', {orgName})
    MySQL.query.await('DELETE FROM `organizations_notes` WHERE `org` = ?', {orgName})

end, true, {help = ('Reset organization to default'), validate = true, arguments = {
    {name = 'orgName', help = ('Organization name'), type = 'string'}
}})

ESX.RegisterCommand('orgmenu', {'user'}, function(xPlayer, args, showError)
    if exports["pc_organizations"]:hasOrgJob(xPlayer.source) then
        TriggerClientEvent("pc_organizations:OpenTablet", xPlayer.source)
    end
end, true, {help = ('Opens organizations tablet')})
-------------------------------------------------------------------------------------------------------------------
--- WEBHOOKS
-------------------------------------------------------------------------------------------------------------------
local adminWebhook = ""
local zonesWebhook = ""
local drugwarsWebhook = ""

-- These functions are responsible for sending the admin webhook
-- All organizations's actions will be posted to these webhooks for mods/admins
function sendAdminWebhook(org, title, content)
    local content = json.encode({
        avatar_url = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
        username = "PoisonCode organizations ADMIN",  
        embeds = {{
            ["title"] = org.." - "..title,
            ["color"] = 248250,
            ["fields"] = {
                {
                    ["name"] = " ",
                    ["value"] = content,
                    ["inline"] = false
                },
            },
            ["footer"] = {
                ["text"] = org.." @ pc_organizations • "..os.date("%H:%M  %d/%m/%Y"),
                ["icon_url"] = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
            },
        }},
    })

    PerformHttpRequest(adminWebhook, function(err, text, headers) end, 'POST', content, { ['Content-Type'] = 'application/json' })
end

function sendZonesWebhook(title, content)
    local content = json.encode({
        avatar_url = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
        username = "PoisonCode organizations ZONES",  
        embeds = {{
            ["title"] = title,
            ["color"] = 248250,
            ["fields"] = {
                {
                    ["name"] = " ",
                    ["value"] = content,
                    ["inline"] = false
                },
            },
            ["footer"] = {
                ["text"] = "pc_organizations zones • "..os.date("%H:%M  %d/%m/%Y"),
                ["icon_url"] = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
            },
        }},
    })

    PerformHttpRequest(zonesWebhook, function(err, text, headers) end, 'POST', content, { ['Content-Type'] = 'application/json' })
end

function sendDrugwarsWebhook(title, content)
    local content = json.encode({
        avatar_url = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
        username = "PoisonCode organizations DRUGWARS",  
        embeds = {{
            ["title"] = title,
            ["color"] = 248250,
            ["fields"] = {
                {
                    ["name"] = " ",
                    ["value"] = content,
                    ["inline"] = false
                },
            },
            ["footer"] = {
                ["text"] = "pc_organizations drugwars • "..os.date("%H:%M  %d/%m/%Y"),
                ["icon_url"] = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
            },
        }},
    })

    PerformHttpRequest(drugwarsWebhook, function(err, text, headers) end, 'POST', content, { ['Content-Type'] = 'application/json' })
end

-- This function is reposponsible for sending the organization's "private" webhook
-- Only this specific organization's actions will be posted
-- Organization A won't be able to see Organization B's actions
function sendOrgWebhook(org, webhook, title, content)
    local content = json.encode({
        avatar_url = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
        username = "PoisonCode organizations",  
        embeds = {{
            ["title"] = title,
            ["color"] = 248250,
            ["fields"] = {
                {
                    ["name"] = " ",
                    ["value"] = content,
                    ["inline"] = false
                },
            },
            ["footer"] = {
                ["text"] = org.." @ pc_organizations • "..os.date("%H:%M  %d/%m/%Y"),
                ["icon_url"] = "https://github.com/PoisonCodeDEV/images/blob/main/logoneon3.png?raw=true",
            },
        }},
    })

    PerformHttpRequest(webhook, function(err, text, headers) end, 'POST', content, { ['Content-Type'] = 'application/json' })
end
-------------------------------------------------------------------------------------------------------------------
--- ZONES
-------------------------------------------------------------------------------------------------------------------
-- Function responsible for adding items to the player after capturing the zone
function addItem(xPlayer)
    local items = {}

    for i, itemData in ipairs(Config.zoneRewards[math.random(#Config.zoneRewards)]) do
        local count = nil

        if itemData.count then 
            count = itemData.count 
        else 
            count = math.random(itemData.minCount, itemData.maxCount) 
        end

        table.insert(items, {item = itemData.item, count = count})

        xPlayer.addInventoryItem(itemData.item, count)
    end

    return items
end
```

</details>

{% hint style="info" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Tutorials

These tutorials will walk you through all the options available.

<details>

<summary>Adding ox_doorlock support</summary>

Organization is not a job, so if you want use it with doorlock (for example ox\_doorlock) you need to make some modifications.

Navigate to `server/framework/es_extended.lua` modify `IsPlayerInGroup`function on line 41, to look like this:

```lua
function IsPlayerInGroup(player, filter)
    local type = type(filter)

    if type == 'string' then
        if player.job.name == filter then
            return player.job.name, player.job.grade
        end

        if player.get("org").name == filter then
            return player.get("org").name, 0
        end
    else
        local tabletype = table.type(filter)

        if tabletype == 'hash' then
            local grade = filter[player.job.name]

            if grade and grade <= player.job.grade then
                return player.job.name, player.job.grade
            end

            if filter[player.get("org").name] and true and 0 <= 0 then
                return player.get("org").name, 0
            end
        elseif tabletype == 'array' then
            for i = 1, #filter do
                if player.job.name == filter[i] then
                    return player.job.name, player.job.grade
                end

                if player.get("org").name == filter[i] then
                    return player.get("org").name, 0
                end
            end
        end
    end
end
```

Add the doorlock as you normally would for a job which in this case will be organization name. Script will now recognize organizations as jobs, but they will remain independent anywhere else.

<img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FbwkeImTpUlN0sd9wEBpt%2Fdoorlockex.png?alt=media&amp;token=7c76333c-1ead-4e8b-8524-9abb00fb319e" alt="" data-size="original">

</details>

<details>

<summary>Adding and removing players from organizations</summary>

You can use `/setorg [id] [org_name]`to add player to organization. Adding player this way will grant them all permissions, so it's recommended only for the leader. Leader can hire new members through tablet.

If you want to remove a player from an organization use `/setorg [id] unemployed` Members can also be removed through tablet with the fire option.

Being (or not) in an organization does not affect player's job

</details>

<details>

<summary>Resetting (clearing) the organization</summary>

If, for whatever reason, you want to reset organization (reset all upgrades to 0, remove items & members etc.) you can use `/resetorg [org_name]`command.

</details>

<details>

<summary>Removing PolyZone integration</summary>

Soon\*

</details>


# Updates

Page dedicated to update trailers and hisotry.

### Update history

<details>

<summary>Update 1.1</summary>

• New command for opening organization tablet /orgmenu \
• Option to open tablet with ox\_target \
• New config option for choosing what can be accessed with target \
• Two new server exports `hasOrgJob` and `openTablet` \
• New client export `openTablet` \
• New server event `pc_organizations:openTablet` \
• Minor fixes in the backend code \
• Updated gitbook docs

</details>


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


# Version compatibility

Page with information helpful for converting your config files to a new version

Most of the times when there's an update you can modify existing config files, so you don't have to configure everything from the start.

<details>

<summary>Updating from 1.0 to 1.1</summary>

Download 1.1 version and replace new utils and config files with your old files.\
Modify your old files:

**config/config.lua : You need to add this anywhere in the file**

```lua
-- Here you can choose which options should be available in ox_target
-- You can still control the access to these options with permissions in Config.Permissions
Config.target = {
    tablet = true, -- enables opening the tablet with ox_target
    safeCheck = true, -- enables checking the organization's safe with ox_target
    safeDeposit = true, -- enables depositing and withdrawing money from the organization's safe with ox_target
    safeWithdraw = true, -- enables withdrawing money from the organization's safe with ox_target
    warehouse = true, -- enables opening the warehouse with ox_target
    wardrobe = true, -- enables opening the wardrobe with ox_target
    wardrobeAdd = true, -- enables adding outfits to the wardrobe with ox_target
    wardrobeRemove = true, -- enables removing an outfit from the wardrobe with ox_target
}
```

\
**config/translations.lua :** **You need to add this anywhere in the file**

```lua
openTablet = "Open tablet",
```

\
**server/utils.lua :** **You need to add this anywhere in the file**

```lua
ESX.RegisterCommand('orgmenu', {'user'}, function(xPlayer, args, showError)
    if exports["pc_organizations"]:hasOrgJob(xPlayer.source) then
        TriggerClientEvent("pc_organizations:OpenTablet", xPlayer.source)
    end
end, true, {help = ('Opens organizations tablet')})
```

</details>


# pc\_skills

Versatile skill system with clean UI that's easy to integrate into existing resources

## Video preview:

{% embed url="<https://youtu.be/iqg31Gd-LB0>" %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Overview of pc\_skills features

<table data-view="cards" data-full-width="true"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>High customizability</strong></em><strong> ⚙️</strong></td><td>You can easily adjust this resource to your needs! (click this card to view config file preview)</td><td><a href="/scripts/pc_airdrop/open-files-preview">Open files preview</a></td></tr><tr><td><em><strong>High performance</strong></em><strong> 💪</strong></td><td>This resource has almost no impact on performance</td><td></td></tr><tr><td><em><strong>Seamless config modifications</strong></em> ⚒️</td><td>If you want to add a new skill, all you need to do is add it to config. Synchronization and database entires happen automatically, so you don't have to worry!</td><td><a href="/pages/Veg2Rkm32HVnpij6OcQc#server_utils.lua">/pages/Veg2Rkm32HVnpij6OcQc#server_utils.lua</a></td></tr></tbody></table>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>Be a part of our community</strong></em> 😎</td><td>When you buy our product you not only become our customer, but a part of our community. We strive for the best possible customer experience, if you have any suggestions regarding existing resources or ideas for future relases -  don't hesitate to contact us, we'll be happy to hear your thoughts!</td><td></td><td></td></tr><tr><td><em><strong>Active support team</strong></em><strong> ⚒️</strong></td><td>When you purchase a resource from our store, you'll be granted a special role on our discord, giving you access to quick support in case you encounter any problems/bugs with our script.</td><td></td><td><a href="https://discord.gg/rNJ8cHXCsN/">https://discord.gg/rNJ8cHXCsN/</a></td></tr></tbody></table>

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FHx5tnb7e32EgbqkzGJIO%2Fskillmenu.png?alt=media&amp;token=508ef06a-4104-439e-bfba-dea4d310a6aa" alt=""><figcaption><p><em>Skills menu</em></p></figcaption></figure>

***

<div><figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FeeZ54B9T8nVZgypMHmEa%2Flvlexample.png?alt=media&amp;token=27aa6aba-0c77-4c0b-940b-a87a96eb5426" alt=""><figcaption><p><em>Lvl-up popup example</em></p></figcaption></figure> <figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FgEeSQlkk8MSHdjj28gOa%2Fxpexample.png?alt=media&amp;token=b650d0f7-985b-4530-91b0-122d0ca5e94c" alt=""><figcaption><p>Gaining xp popup example</p></figcaption></figure></div>

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

Quick installation guide for pc\_skills

{% hint style="info" %}
**Lua knowledge recommended**

This resource provides functions that need to be integrated into other resources. It's fairly straightforward, but if you don't have experience with modifying fivem scripts you might struggle to get it to work. You can always contact support if you experience any problems.
{% endhint %}

### Dependencies & integrations

This script interacts with some other resources:

|                                                                                                                                                 Resource                                                                                                                                                 |                     Recommended version                     | Required |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------: | :------: |
| <p><strong>One</strong> of supported frameworks:<br>• ESX: <a href="https://github.com/esx-framework/esx_core">es\_extended</a><br>• QBCore: <a href="https://github.com/qbcore-framework/qb-core/tree/main">qb-core</a><br>• QBox: <a href="https://github.com/Qbox-project/qbx_core">qbx\_core</a></p> | <p>• ESX: 1.6.0+<br>• QBCore: 1.2.6+<br>• QBox: 1.22.3+</p> |    Yes   |
|                                                                                                                            [oxmysql](https://github.com/overextended/oxmysql)                                                                                                                            |                           2.12.0+                           |    Yes   |

*"Recommended version" only tells you what version of the resource has been tested, other versions (including older ones) will likely work as well.*

### Installing the resource

{% stepper %}
{% step %}

### Add the resource to your server

Upload the file into your resources folder

Load `database.sql` into your database

Add the script by adding following to the server.cfg: *`ensure pc_skills`*
{% endstep %}

{% step %}

### Integrate into existing resources

Use exports and events in other resources to utilize pc\_skills functions. More info in `Exports & events` tab
{% endstep %}

{% step %}

### Restart your server

Restart the server for the change to take effect and check for any errors
{% endstep %}
{% endstepper %}


# Exports & events

Necessary exports and events available in this resource

## Client side

<details>

<summary><strong>Opening the skill menu</strong></summary>

```lua
TriggerEvent('pc_skills:client:openSkillMenu')
```

```lua
exports.pc_skills:openSkillMenu()
```

Skills menu will open for player that triggers the export/event. There's also a /nui command

</details>

<details>

<summary><strong>Checking the skill level or xp</strong> </summary>

```lua
exports.pc_skills:getSkill("skillName", "type")
```

• skillName - string - skill name, for example - "skill1" \
• type - string - "xp", "lvl" or "\*" - depending on what you want to check

returns:\
\> An int value when using "xp" or "lvl"\
\> Table containing xp and lvl when using "\*"&#x20;

</details>

For safety reasons, setting lvl/xp or giving xp is possible only on server side.

## Server side

<details>

<summary><strong>Opening the skill menu for given player</strong></summary>

```lua
TriggerEvent('pc_skills:client:openSkillMenu', player)
```

```lua
exports.pc_skills:openSkillMenu(player)
```

• player - playerId -  id of the player you want to open menu for

Skills menu will open for target player

</details>

<details>

<summary><strong>Checking the skill level or xp of given player</strong></summary>

```lua
exports.pc_skills:getSkill(player, "skillName", "type")
```

• player - playerId - id of the player you want to check\
• skillName - string - skill name, for example - "skill1" \
• type - string - "xp", "lvl" or "\*" - depending on what you want to check

returns:\
\> An int value when using "xp" or "lvl"\
\> Table containing xp and lvl when using "\*"&#x20;

</details>

<details>

<summary>Setting xp or level for given player</summary>

```lua
exports.pc_skills:setSkill(player, "skillName", 'type', value)
```

• player - playerId - id of the player you want to set lvl/xp for\
• skillName - string - skill name, for example - "skill1" \
• type - string - "xp" or "lvl" - depending on what you want to set\
• value - int - number you want to set

example:\
exports.pc\_skills:setSkill(1, "skill1", 'lvl', 10) - sets lvl 10 of skill1 for player with id 1

</details>

<details>

<summary>Adding xp for given player</summary>

```lua
exports.pc_skills:giveXP(player, "skillName", value)
```

• player - playerId - id of the player you want to give xp to\
• skillName - string - skill name, for example - "skill1" \
• value - int - xp amount you want to give

example:\
exports.pc\_skills:giveXP(1, "skill1", 10) - gives 10 xp in skill1 to player with id 1

</details>


# Open files preview

Config files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is customizable, there is a total of 2 files accessible for you.
{% endhint %}

## config/config.lua

<details>

<summary>Click here to see a preview config.lua</summary>

```lua
Config = {
    framework = "ESX", -- available: "ESX", "QBCore", "QBox"
    debug = true, -- enable/disables debug mode (errors will be printed either way)
    notificationPosition = "bottom-center",  -- options: "top-left", "top-center", "top-right", "middle-left", "middle-center", "middle-right", "bottom-left", "bottom-center", "bottom-right"
    autoSaveDelay = 10, -- minutes between auto-saves

    skills = {
        {
            name = "skill1", -- "technical" skill name, can be whatever you'd like, but it has to be one word, and you can't change it later
            label = "JOB", -- label that will be shown in the skill menu/pop-ups
            default_lvl = 1, -- default "starting" level
            default_xp = 0, -- default "starting" xp
            max = 15, -- max skill level
            icon = "fa-solid fa-briefcase", -- icon used for the skill https://fontawesome.com/v6/icons?o=r&s=solid
            modalTitle = "JOB", -- title that will be shown in the window after hovering over the skill
            modalContent = "Job example text", -- text that will be shown in the window
            threshold = { -- xp thresholds for each level
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300
            }
        },
        {
            name = "skill2",
            label = "production",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-flask",
            modalTitle = "DRUG PRODUCTION",
            modalContent = "Drug production example text",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill3",
            label = "HUSTLE",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-comments-dollar",
            modalTitle = "HUSTLE",
            modalContent = "Hustle example text",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill4",
            label = "DRUG DEALING",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-cannabis",
            modalTitle = "DRUG DEALING",
            modalContent = "Drug dealing example text",
            threshold = {
                [2] = 300, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill5",
            label = "LUCK",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-star",
            modalTitle = "LUCK",
            modalContent = "Luck example text",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill6",
            label = "FISHING",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-fish",
            modalTitle = "FISHING",
            modalContent = "Fishing example text",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill7",
            label = "INTELLIGENCE",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-brain",
            modalTitle = "INTELLIGENCE",
            modalContent = "Intelligence example text",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill8",
            label = "HEALTH",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-heart",
            modalTitle = "HEALTH",
            modalContent = "Health example text",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
        {
            name = "skill9",
            label = "OTHER",
            default_lvl = 1,
            default_xp = 0,
            max = 20,
            icon = "fa-solid fa-heart",
            modalTitle = "OTHER",
            modalContent = "Other example text2",
            threshold = {
                [2] = 200, [3] = 400, [4] = 600, [5] = 800,
                [6] = 1000, [7] = 1200, [8] = 1400, [9] = 1600,
                [10] = 1800, [11] = 2100, [12] = 2400, [13] = 2700,
                [14] = 3000, [15] = 3300, [16] = 3400, [17] = 3800,
                [18] = 4200, [19] = 4600, [20] = 5000
            }
        },
    }
}
```

</details>

## config/translations.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io

Translations = {
    skills = "Skills",
    lvlup = "Level Up!",
    lvlupDesc = "Congratulations! You've reached Level:",
}
```

</details>

{% hint style="info" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Tutorials

These tutorials will walk you through all the options available.

<details>

<summary><strong>Adding a new skill</strong></summary>

Adding a new skill is very easy and can be done at any time, regardless if you're setting up the script for the first time or if you have been using it for a long time.

* Add the new skill to config with a <mark style="color:yellow;">**unique name**</mark> and set up all the options.
* Restart the script (or server)
* Done! All database changes and synchronizations will happen automatically on the backend!

</details>


# Updates

Page dedicated to update trailers and hisotry.

### No updates yet


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


# pc\_airdrop

This is one of the most advanced scripts for the airdrop system with great performance. It can be configured in many different ways and used in lots game modes. Check it out yourself!

## Video preview:

{% embed url="<https://www.youtube.com/watch?v=stl7tziz4eg>" %}
*See this script in action!*
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Overview of pc\_airdrop features

<table data-view="cards" data-full-width="true"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>High customizability</strong></em><strong> ⚙️</strong></td><td>You can easily adjust this resource to your needs! (click this card to view config file preview)</td><td><a href="/scripts/pc_airdrop/open-files-preview">Open files preview</a></td></tr><tr><td><em><strong>High performance</strong></em><strong> 💪</strong></td><td>This asset was built with high performance in mind - It's unnoticeable when idle and very efficient when being used!</td><td></td></tr><tr><td><em><strong>Discord notifications</strong></em><strong> 🤖</strong></td><td>This script comes with discord webhook notifications built in!<br>All you need to do is add your own webhook url and you're good to go!</td><td><a href="/pages/Veg2Rkm32HVnpij6OcQc#server_utils.lua">/pages/Veg2Rkm32HVnpij6OcQc#server_utils.lua</a></td></tr></tbody></table>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>Be a part of our community</strong></em> 😎</td><td>When you buy our product you not only become our customer, but a part of our community. We strive for the best possible customer experience, if you have any suggestions regarding existing resources or ideas for future relases -  don't hesitate to contact us, we'll be happy to hear your thoughts!</td><td></td><td></td></tr><tr><td><em><strong>Active support team</strong></em><strong> ⚒️</strong></td><td>When you purchase a resource from our store, you'll be granted a special role on our discord, giving you access to quick support in case you encounter any problems/bugs with our script.</td><td></td><td><a href="https://discord.gg/rNJ8cHXCsN/">https://discord.gg/rNJ8cHXCsN/</a></td></tr></tbody></table>

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FD9hCJmThPSLdpCPd42z2%2F3.png?alt=media&amp;token=fa8a8f59-108c-4a48-b5a6-feb2709cd36c" alt=""><figcaption><p><em>Airdrop in the sky and flare on the landing spot</em></p></figcaption></figure>

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FELpOp16d0JYw2VGMqfGI%2F2.png?alt=media&amp;token=1812fd4d-9d16-4c56-8510-6251e524a4f6" alt=""><figcaption><p><em>Airdrop after landing</em></p></figcaption></figure>

***

<div><figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FovZQGZYdIZ6STocum1k0%2F1.png?alt=media&amp;token=d997b283-6eec-4a79-b118-94310c8b143f" alt="" width="349"><figcaption><p><em>Discord webhook notification 1</em></p></figcaption></figure> <figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FaL9zm8byT4hIYTBuBDKI%2F4.png?alt=media&amp;token=fd61b740-a5a0-4baa-8b57-76f72ca52e6d" alt=""><figcaption><p><em>Discord webhook notification 2</em></p></figcaption></figure></div>

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

Quick installation guide for pc\_airdrop

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however to get the most out of it, basic knowledge of lua and scripts configuration is very useful.
{% endhint %}

**1.** Upload the file into your resources folder\
\
**2.** Start the script, by adding this to your server.cfg:\
*`ensure pc_airdrop`*


# Open files preview

Config & Utils files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is highly customizable, there is a total of 4 files accessible for you.
{% endhint %}

## config/config.lua

<details>

<summary>Click here to see a preview config.lua</summary>

```lua
Config = {} 
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io


----- This script is highly customizable, plase take your time and adjust it to your needs! ------
--------------------------------------------------------------------------------------------------
--  --------------- If you encounter any bugs, please report it on our discord ---------------- --
-- Documentation available at: https://poisoncode.gitbook.io/poisoncode-docs/scripts/pc_airdrop --
--------------------------------------------------------------------------------------------------

-- BY DEFAULT REWARD ITEMS ARE GIVEN USING ESX OR QBCORE FUNCTION. 
-- SEE utils/server.lua TO MODIFY FUNCTION RESPONSIBLE FOR ADDING ITEMS TO INVENTORY!

Config.UseExperimentalFeatures = false -- If you want to use experimental features, set this to true. (Experimental features are not fully tested and may not work as expected)

Config.ClientDebugMessages = false -- Enables additional debug messages on the client side console (F8), we recommend turning this off while not debugging 
Config.ServerDebugMessages = true -- Enables additional debug messages on the server side console, you can keep it on if these messages don't bother you
-- ^^ (Errors and warnings will always be displayed) ^^


--   Technical  --
Config.Framework = "NEW-ESX"      -- "NEW-ESX" - "ESX" - "QBCore"
Config.UseCollision2 = false       -- If you're experiencing an error where the airdrop gets stuck in the air, set this to true, otherwise it's not recommended


--    Basic     --
Config.CaptureTime = 15             -- Time (in seconds) required to capture an airdrop
Config.CaptureDistance = 10         -- How far can you go before airdrop capture is canceled
Config.AirdropFallSpeed = 1       -- How quick should the airdrop fall. Default 1 (Slowest setting) (Used only when Config.AirdropGroundSpawn is false)
Config.AirdropGroundSpawn = false   -- Should the airdrop spawn directly on the ground (Without falling down from the sky)
Config.LocationNotification = true  -- If set to true, notification will contain airdrop name and location name


-- Discord logs --
Config.DiscordLogs = false         -- Set to true if you want to recieve discord logs. (set your webhook in server utils)


-- Integrations --
Config.Use_Target = false           -- If you set this option to true, collecting the airdrop will be possible only using qTarget or ox_target
Config.Target = "ox_target"         -- "qtarget" or "ox_target" or "qb-target"
Config.Use_OxInventory = false      -- Turns on ox_inventory integration
Config.RemoveWhenEmpty = false      -- When using ox_inventory, should the airdrop crate be removed when players take all the items
Config.Use_pcnotifications = GetResourceState('pc_notifications') == 'started' or false -- Automatically turns the pc_notification on or off


--  Advanced    --
Config.Plane = {
    enable = true,                 -- Should there be a plane flying by and dropping the airdrop
    distance = 500,                -- How far should the plane spawn (bigger value = longer fly time)
    speed = 50,                    -- How fast should the plane travel
}


Config.AirdropRemove = {
    enable = true,                 -- Removes the airdrop after a period of time. Especially recommended if you're using ox_inventory integration
    time = 1200,                   -- Time (in seconds) after the airdrop is automatically removed from the map (example 1200 sec = 20 min)
}


Config.AirdropLock = {
    enable = true,                 -- After falling/spawning, should the airdrop be locked
    showtext = true,               -- Should there be 3D/Helpnotification/custom text showed when the airdrop is locked AirdropLock.enable should be set to true
    time = 15,                     -- How long should the airdrop be locked for after falling/spawning
}


Config.Models = {
    -- You can change crate and/or parachute prop model. List of all props: https://gta-objects.xyz/objects/
    -- Warning: some models might not work or have different physics. Change at your own risk
    crate = "xm_prop_rsply_crate04a",
    parachute = "p_parachute1_mp_dec",

    -- Plane model
    plane = "titan",

    -- Ped model for pilot. List of all peds: https://docs.fivem.net/docs/game-references/ped-models/ 
    npc_pilot = "s_m_m_pilot_02",
}


Config.ShowFlares = {
    [1] = true, -- Should there be a "flare effect" on the ground when the airdrop is falling ? (true/false)
    [2] = true, -- Should there be a "flare + smoke effect" on the airdrop crate when it lands on the ground ? (true/false)
}


-- Basic methods of spawning an airdrop
Config.AirdropSpawnMethod = {
    event_enabled = false, -- Creates a custom server event for calling an airdrop (customize in utils/server.lua)

    command_enabled = true, -- Should you be able to call command /airdrop (customize in utils/server.lua) WARNING: BY DEFAULT COMMAND IS AVAILABLE FOR EVERYONE

    auto_enabled = false, -- Should there be an airdrop every X minutes (Set cooldown below)
    auto_delay = 10, -- After server/script restart, first airdrop will be called in after this delay (in minutes) DEFAULT: 10 MINUTE DELAY
    auto_timer = 60, -- Time in minutes between airdrops. DEFAULT: AIRDROP EVERY 60 MINUTES
}


-- If you want your airdrop to be called in a random spot within provided coords and radius set Config.AirDropInRadius to true.
-- If you set Config.AirDropInRadius to false, the airdrop will always land on provided coords, and radius will be ignored (we suggest to disable radius blip if in that case).
-- Airdrop location will be choosen at random from coords provided below, unless a forced location will be provided with a command or through event.
-- Bear in mind that this script does not check if location is safe, it is possible that the airdrop falls in water. Be cautious when setting spawn radius
Config.AirDropInRadius = true 
Config.Locations = {
    {label = "Location1", coords = vector3(3678.45, 4983.75, 15.83), radius = 50},
    {label = "Location2", coords = vector3(1450.03, 4408.11, 46.77), radius = 50},
    {label = "Location3", coords = vector3(112.3445, 6827.7622, 17.0759), radius = 80},
    {label = "Location4", coords = vector3(2504.6265, 3264.5303, 52.1214), radius = 50},
    {label = "Location5", coords = vector3(1573.6575, 3196.3611, 40.5308), radius = 70},
    {label = "Location6", coords = vector3(334.0565, 2849.8423, 43.4093), radius = 60},
    {label = "Location7", coords = vector3(-2171.0454, 3063.4260, 32.9654), radius = 60},
    {label = "Location8", coords = vector3(-1334.5094, -3040.9614, 13.9444), radius = 100},
    {label = "Location9", coords = vector3(1373.5991, -745.4987, 67.2328), radius = 60},
}


-- Configure airdrops and their loot, type of spawned airdrop is random.
-- ItemName is the name used to give the item to the player.
-- ItemAmount OR ItemAmountMin & ItemAmountMax is the amount of this item that should be awarded to player.
-- If using ItemAmountMin & ItemAmountMax the amount will be chosen at random between these two values
-- Chance is the chance (1-100) that the player recieves this item. If set to 100, player will always recieve this reward.
-- Type "item" or "weapon" (will be ignored if using ox_inventory), used when you DONT use "weapons as items" (you can only have one weapon of given type for example).
Config.AirDrops = {
    [1] = { -- Airdrop id number (has to bee different for each airdrop)
        ['name'] = "Medical", -- Name of the airdrop that will be shown in notifications
        ['items'] = { -- Loot table
            {ItemName = "bandage", ItemAmountMin = 5, ItemAmountMax = 10, Chance = 100, Type = "item"}, -- items, amount and chance
            {ItemName = "medikit", ItemAmount = 1, Chance = 90, Type = "item"},
        },
        },
    [2] = {
        ['name'] = "Weapons", -- Name of the airdrop that will be shown in notifications
        ['items'] = { -- Loot table
            {ItemName = "weapon_pistol", ItemAmount = 1, Chance = 100, Type = "item"}, -- <- if not using ox_inventory or other "weapons as items" script, change type to "weapon"
            {ItemName = "weapon_pistol50", ItemAmount = 1, Chance = 10, Type = "item"},
        },
        },
    [3] = {
        ['name'] = "Drugs", -- Name of the airdrop that will be shown in notifications
        ['items'] = { -- Loot table
            {ItemName = "weed", ItemAmount = 10, Chance = 100, Type = "item"},
            {ItemName = "coke", ItemAmountMin = 5, ItemAmountMax = 15, Chance = 50, Type = "item"},
        },
    },
}


-- List of blip IDs and colors - https://docs.fivem.net/docs/game-references/blips/
Config.blips = {
    main = { 
    ['enabled'] = true, -- Should the main blip be on ? (true/false)
    ['scale'] = 1.2, -- Scale of the main blip
    ['blipId'] = 568, -- Blip (See link above for reference)
    ['color'] = 81, -- Blip color (See link above for reference)
    ['type'] = 6, -- For blip types, please reference: https://docs.fivem.net/natives/?_0x9029B2F3DA924928
    ['name'] = "Airdrop", -- Label of the main blip on the map
    },

    radius = {
    ['enabled'] = true, -- Should the radius blip be on ? (true/false)
    ['color'] = 81, -- Radius blip color (See link above for reference)
    ['alpha'] = 100, -- Radius blip transparency (higher number = lower transparency)
    },
}


Config.Marker = {
    enabled = true, -- Should there be a circle marker showing how far can you go when capturing an airdrop
    red = 0, green = 125, blue = 255, -- You can customize the color of the marker by adjusting RGB (0-255)
}


-- How should "collect" text be showed. Available types:
-- 0 - No 3D Text or notification will be displayed
-- 1 - 3D text is being displayed on the airdrop crate (This option is slightly less efficient, if you care about performance use another type)
-- 2 - Help notofication is being displayed in the top left corner.
-- 3 - Will run code from CustomCaptureFuntion in utils/client.lua.
-- See https://poisoncode.gitbook.io/poisoncode-docs/scripts/pc_airdrop for reference
Config.captureTextType =  1


-- [[Notifications functions has been moved to: utils/client.lua]]
-- [[Custom event has been moved to utils/client.lua]]
```

</details>

## config/translations.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io
 
Translations = {
    base = {
        -- Text showed during airdrop capture
        ["collect"] = "Press ~y~E ~w~to capture the airdrop", -- Text shown before player started capturing the airdrop
        ["collecting"] = "Airdrop is being captured. Remaining: ~b~%s ~w~sec",

        -- Used only when Config.AirdropGroundTextLabel is true
        ["airdrop_locked"] = "~r~Airdrop locked for: ~y~%s ~w~sec",

        -- Notifications text
        ["notification_inbound"] = "Airdrop has been called!",
        ["notification_inbound_2"] = "%s airdrop was called to %s!",
        ["notification_startcapture"] = "You are capturing the airdrop!",
        ["notification_captured"] = "You captured the airdrop!",
        ["notification_capturecanceled"] = "You cancelled airdrop capture!",
        ["notification_someonecaptured"] = "Someone captured the airdrop!",
        ["notification_cantcapture"] = "This airdrop is already being captured!",

        -- Target event label
        ["TargetText"] = "Capture the airdrop", 

        -- Ox_inventory stash label
        ["oxinv_label"] = "Airdrop",
    },

    -- Discord logs
    discord = {
        -- Titles
        ["title_auto"] = "Airdrop called automatically",
        ["title_command"] = "Airdrop called by command",
        ["title_event"] = "Airdrop called by event",
        ["title_captured"] = "Airdrop captured",
        ["title_remove"] = "Airdrop removed",

        -- Desc
        ["call_auto"] = "Airdrop was called in automatically!",
        ["call_command"] = "called an airdrop using a command!",
        ["call_event"] = "Airdrop was called in by event!",
        ["captured_by"] = "Airdrop was captured by: ",
        ["type"] = "Type",
        ["location"] = "Location",
        ["airdrop_remove"] = "Airdrop crate was automatically removed.",
        ["airdrop_remove_items"] = "Airdrop crate was removed because all items were taken.",
    }

}
```

</details>

## client/utils.lua

<details>

<summary>Click here to see a preview client.lua</summary>

```lua
-- [1. Main functions]
-- a) Function responsible for sending the notifications
function SendNotification(text, type)
    local time = 5000 -- how long should notification be on screen (in ms)
    if Config.Use_pcnotifications then
        exports['pc_notifications']:Notify(type, 'Airdrop', text, time, true)
    else
        -- Other notification system
    end
end


-- b) Joining player sync -- EXPERIMENTAL - only works when Config.UseExperimentalFeatures is set to true
AddEventHandler('playerJoinedEvent',function()
    TriggerServerEvent('pc_airdrop:server:requestAirdropSync')
end)



-- [2. Event functions]
-- a) This function is triggered when the airdrop is called
function onAirdropCall(type, location)
    if Config.LocationNotification and location then
        SendNotification((Translations.base["notification_inbound_2"]):format(type, location), 'info')
    else
        SendNotification(Translations.base["notification_inbound"], 'info')
    end
    -- *CUSTOM CODE*
    -- *EVENT TRIGGER* 
    -- ETC.
end

-- b) This function is triggered when the airdrop hits the ground (Does not trigger if Config.AirdropGroundSpawn is on)
function onAirdropHit()
    --SendNotification("Airdrop hit the ground!", 'info')
end

-- c) This function is triggered when the airdrop unlocks (Only when airdrop lock is enabled in Config)
function onAirdropUnlock()
    --SendNotification("Airdrop has been unlocked!", 'info')
end

-- d) This function is triggered when you start capturing the airdrop
function onCaptureStart() 
    SendNotification(Translations.base["notification_startcapture"], 'info')
end

-- e) This function is triggered when you successfully capture the airdrop
function onAirdropCapture() 
    SendNotification(Translations.base["notification_captured"], 'success')
end
 
-- f) This function is triggered when someone else successfully captures the airdrop
function onCaptureSomeone()
    SendNotification(Translations.base["notification_someonecaptured"], 'info')
end 

-- g) This function is triggered when you cancel capturing the airdrop
function onCaptureCancel()
    SendNotification(Translations.base["notification_capturecanceled"], 'error')
end

-- h) This function is triggered when you try to capture the airdrop but you can't
function onCaptureTryFail()
    SendNotification(Translations.base["notification_cantcapture"], 'error')
end


-- [3. Integration/technical functions]
-- a) 3D Text function, you can customize font, size etc.
function Draw3DText(x, y, z, scl_factor, text)
    local onScreen, _x, _y = World3dToScreen2d(x, y, z + 1.0)
    local p = GetGameplayCamCoords()
    local distance = GetDistanceBetweenCoords(p.x, p.y, p.z, x, y, z + 1.0, 1)
    local scale = (1 / distance) * 2
    local fov = (1 / GetGameplayCamFov()) * 100
    local scale = scale * fov * scl_factor
    if onScreen then
        SetTextScale(0.0, scale)
        SetTextFont(4)
        SetTextProportional(1)
        SetTextColour(255, 255, 255, 215)
        SetTextDropshadow(0, 0, 0, 0, 255)
        SetTextEdge(2, 0, 0, 0, 150)
        SetTextDropShadow()
        SetTextOutline()
        SetTextEntry("STRING")
        SetTextCentre(1)
        AddTextComponentString(text)
        DrawText(_x, _y)
    end
end

-- b) Help Notification function, not much to customize
function ShowHelpNotification(text)
    SetTextComponentFormat("STRING")
    AddTextComponentString(text)
    DisplayHelpTextFromStringLabel(0, 0, 1, -1)
end

-- c) Custom function that should be ran when Config.captureTextType is set to 3
function CustomCaptureFuntion(text)
    print(text) -- EXAMPLE
     -- You can add custom code here to for example: trigger a progress bar
end

-- d) Blips customization
function CreateBlips(coords, radius)
    -- Main blip
	if Config.blips.main['enabled'] then
		blips[1] = AddBlipForCoord(coords)
		SetBlipScale(blips[1], Config.blips.main['scale'])
		SetBlipSprite(blips[1], Config.blips.main['blipId'])
		SetBlipHighDetail(blips[1], true)
		SetBlipColour(blips[1], Config.blips.main['color'])
		SetBlipDisplay(blips[1], Config.blips.main['type'])
		BeginTextCommandSetBlipName("STRING")
		AddTextComponentString(Config.blips.main['name'])
		EndTextCommandSetBlipName(blips[1])
	end

    -- Radius blip
	if Config.blips.radius['enabled'] and radius then
		blips[2] = AddBlipForRadius(coords, radius + 10.0)
		SetBlipSprite(blips[2], 9)
		SetBlipColour(blips[2], Config.blips.radius['color'])
		SetBlipAlpha(blips[2], Config.blips.radius['alpha'])
	end
end

-- [4. Targeting system integration]
if Config.Use_Target then
	if Config.Target == "qtarget" then
		exports.qtarget:AddTargetModel({GetHashKey(Config.Models.crate)}, {
			options = {
				{	
					event = "pc_airdrop:client:targetcollect",
					icon = "fas fa-hand",
					label = Translations.base["TargetText"],
					canInteract = function()
						return not capturing_me and not capturing_someone and lock_timer <= 0
					end
				},
			},
			distance = 5.0,
		})
	elseif Config.Target == "ox_target" then
		exports.ox_target:addModel({GetHashKey(Config.Models.crate)}, {
			name = 'Airdrop',
			label = Translations.base["TargetText"],
			icon = "fas fa-hand",
			event = "pc_airdrop:client:targetcollect",  
			canInteract = function()
				return not capturing_me and not capturing_someone and lock_timer <= 0
			end,
			distance = 5.0,    
		})
	elseif Config.Target == "qb-target" then
        exports['qb-target']:AddTargetModel(GetHashKey(Config.Models.crate), {
            options = {
                {
                    type = "client",
                    event = "pc_airdrop:client:targetcollect",
					icon = "fas fa-hand",
                    label = Translations.base["TargetText"],
					canInteract = function()
						return not capturing_me and not capturing_someone and lock_timer <= 0
					end
                },
            },
            distance = 5.0,
        })
	end
end
```

</details>

## server/utils.lua

<details>

<summary>Click here to see a preview server.lua</summary>

```lua
-- [1. Framework + integrations]
Framework = nil

if Config.Framework == "NEW-ESX" then
    Framework = exports["es_extended"]:getSharedObject()
elseif Config.Framework == "ESX" then
    TriggerEvent('esx:getSharedObject', function(library) 
        Framework = library 
    end)
elseif Config.Framework == "QBCore" then
    Framework = exports["qb-core"]:GetCoreObject()
end
                           
function RewardItems(src, item, amount, type)
    -- If you're using esx:
    if Config.Framework == "NEW-ESX" or Config.Framework == "ESX" then
        local xPlayer = Framework.GetPlayerFromId(src)
        if xPlayer then
            if type ~= "weapon" then -- adding an item
                xPlayer.addInventoryItem(item, amount)
                -- You can add notifications here if you'd like
            else -- adding a weapon
                xPlayer.addWeapon(item, amount)
                -- You can add notifications here if you'd like
            end
        end

    -- if you're using qb-core:
    elseif Config.Framework == "QBCore" then
        local xPlayer = Framework.Functions.GetPlayer(src)
        if xPlayer then
            xPlayer.Functions.AddItem(item, amount)
            -- You can add notifications here if you'd like
        end
    end
end

-- ox_inventory integration (if you're using other grid based inventory system, you can modify this part of the code) 
-- don't touch it if you don't know what you're doing
if Config.Use_OxInventory then

    local ox_inventory = exports.ox_inventory

    ox_inventory:RegisterStash('airdrop', Translations.base["oxinv_label"], 50, 100000, false)

    function oxinvAddItem(itemName, itemCount)
        local inventory = ox_inventory:GetInventory({id = 'airdrop'})
        ox_inventory:AddItem(inventory.id, itemName, itemCount)
    end

    function oxinvClearInv()
        local inventory = ox_inventory:GetInventory({id = 'airdrop'})
        ox_inventory:ClearInventory(inventory)
    end

end


-- [2. Airdrop spawning]
-- COMMAND
-- WARNING: THIS COMMAND IS AVAILABLE FOR EVERYONE BY DEFAULT, CHANGE THE CODE BELOW TO ONLY ALLOW CERATIN PEOPLE TO CALL THE AIRDROP!
if Config.AirdropSpawnMethod['command_enabled'] then
    RegisterCommand("airdrop", function(source, args, rawCommand)
        createNewAirdrop({type = "command", src = source, arg = args})
    end)
end


-- EVENT
-- IF SET TO TRUE IN CONFIG "pc_airdrop:server:createNewAirdrop" SERVER EVENT WILL BE CREATED
-- YOU CAN CREATE YOUR OWN WAY OF CALLING AN AIRDROP USING THIS EVENT
if Config.AirdropSpawnMethod['event_enabled'] then
    RegisterNetEvent("pc_airdrop:server:createNewAirdrop", function(args)
        createNewAirdrop({type = "event", src = source, arg = args})
    end)
end
 

-- [3. Discord logs]
if Config.DiscordLogs then 

    WebhookURL = "" -- INSERT YOU DISCORD WEBHOOK HERE

    function DiscordLog(title,desc,color)
        PerformHttpRequest(WebhookURL, function(err, text, headers) end, 'POST', json.encode({
            username = "Airdrop - Logs", 
            embeds = {{
                ["color"] = color, 
                ["title"] = title,
                ["description"] = desc,
                ["footer"] = {
                    ["text"] = "PoisonCode Airdrop  •  "..os.date("%x %X %p")
                },
            }} 
        }), { 
            ['Content-Type'] = 'application/json' 
        })
    end
end
```

</details>

{% hint style="danger" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Tutorials

These tutorials will walk you through all the options available.

## Config.lua tutorial:

{% embed url="<https://www.youtube.com/watch?v=iC7wfVIm5KE>" %}
Config tutorial
{% endembed %}

## Other tutorials:

<details>

<summary>How to enable command only for admins (ESX)</summary>

Modify existing command code in `server/utils.lua` to look like this:

```lua
-- COMMAND
-- WARNING: THIS COMMAND IS AVAILABLE FOR EVERYONE BY DEFAULT, CHANGE THE CODE BELOW TO ONLY ALLOW CERATIN PEOPLE TO CALL THE AIRDROP!
if Config.AirdropSpawnMethod['command_enabled'] then
    RegisterCommand("airdrop", function(source, args, rawCommand)
        local xPlayer = Framework.GetPlayerFromId(source)
        if (xPlayer.group == 'superadmin' or xPlayer.group == 'admin' or xPlayer.group == 'mod') then
            createNewAirdrop({type = "command", src = source, arg = args})
        end
    end)
end
```

You can add/remove groups as required

</details>

<details>

<summary>How to enable command only for admins (QBcore/Qbox)</summary>

Soon

</details>

<details>

<summary>How to synchronize airdrops for players joining the server</summary>

Soon

</details>

<details>

<summary>How to spawn airdrop using arguments (choose place and type)</summary>

Soon

</details>


# Updates

Page dedicated to update trailers and hisotry.

### Trailers

{% embed url="<https://youtu.be/vDc8Xc22ZJE>" %}
***1.2 update trailer***
{% endembed %}

### Update history

<details>

<summary>Update 1.3.9</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.8</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.7</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.6</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.5</summary>

Introduced expermiental features:\
• client & server debug messages\
• Synchronization for joining players - [check tutorials](/scripts/pc_airdrop/tutorials#other-tutorials)

</details>

<details>

<summary>Update 1.3.4</summary>

* Target system (ox\_target/qTarget etc.) functions has been moved to client utils so you can integrate other systems or customize existing ones.

</details>

<details>

<summary>Update 1.3.3</summary>

* Airdrop spawn function will now accept arguments - [check tutorials](/scripts/pc_airdrop/tutorials#other-tutorials)

</details>

<details>

<summary>Update 1.3.2</summary>

</details>

<details>

<summary>Update 1.3.1</summary>

• Fixed a bug that blocked opening the airdrop again after the first time (when using target\&ox\_inventory) \
• Added new config option Config.RemoveWhenEmpty that removes the airdrop after all the items has been collected (when using ox\_inventory) \
• Added `["airdrop_remove_items"]` to `translations.lua`

</details>

<details>

<summary>Update 1.3</summary>

• Added blips to utils \
• Added ox\_inventory integration to utils \
• Improved both client & server side code for reliability, performance and security \
• Refactored most of the code for faster future update developement \
• Official QBCore & qb-target support \
• New (optional) notification containing airdrop name and location name \
• Added (optional) min/max item count (amount will be chosen at random between them) \
• Added option to give player weapons while not using ox\_inventory or other "weapons as items" system \
• Added automatic check for pc\_notifications, now you don't need to remove anything if you don't use it

</details>

<details>

<summary>Update 1.2</summary>

* Added a plane that flies by and drops the crate.
* Added ox\_inventory integration.
* Added qTarget/ox\_arget integration.
* Added custom functions triggered by different events (utils/client.lua).
* Added config/translations.lua file and the option to translate discord logs.
* Added the ability to change crate/parachute/plane/pilot model.
* Added the option to lock airdrop for a set period of time after it falls/spawns.
* Added the option to remove the airdrop from the map after a set period of time.
* Lots of bug fixes and optimizations.
* Restructure of folders and files to make it more clear.

</details>

<details>

<summary>Update 1.1</summary>

* Added 4 new config options regarding spawning the airdrop directly on the ground (without falling).
* Added a new collision request that should fix the error where the airdrop gets stuck in the air.
* New error handlers, if you experience any bugs there's a high chance it will show up in the F8 console.
* Minor code cleanup.

</details>


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


# pc\_notifications

Simple yet eye-pleasing notifications, available in 5 different types, each with its own custom sound.

## Video preview:

{% embed url="<https://www.youtube.com/watch?v=WmA-NabDwks>" %}
*See this script in action!*
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com) or [github](https://github.com/PoisonCodeDEV/)


# Features

Quick Overview of pc\_notifications features.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>Be a part of our community</strong></em> 😎</td><td>When you buy our product you not only become our customer, but a part of our community. We strive for the best possible customer experience, if you have any suggestions regarding existing resources or ideas for future relases -  don't hesitate to contact us, we'll be happy to hear your thoughts!</td><td></td><td></td></tr><tr><td><em><strong>Active support team</strong></em><strong> ⚒️</strong></td><td>When you purchase a resource from our store, you'll be granted a special role on our discord, giving you access to quick support in case you encounter any problems/bugs with our script.</td><td></td><td><a href="https://discord.gg/rNJ8cHXCsN/">https://discord.gg/rNJ8cHXCsN/</a></td></tr></tbody></table>

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

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FUtzhGKQ1LNmoSeBhzukl%2Fsuccess.png?alt=media&amp;token=f9a3b7a0-4d05-4584-b3ea-2c501eb15386" alt=""><figcaption><p>Success notification</p></figcaption></figure>
{% endtab %}

{% tab title="Error" %}

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2Fa60RBEA9sf680N9DUQ4W%2Ferror.png?alt=media&amp;token=77ad1669-5247-48b0-ac71-de8a89928866" alt=""><figcaption><p>Error notification</p></figcaption></figure>
{% endtab %}

{% tab title="Warning" %}

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2Fn20moSQQ85GqhI3IDdXf%2Fwarning.png?alt=media&amp;token=926c3032-38df-41db-b743-d9bca000ca47" alt=""><figcaption><p>Warning notification</p></figcaption></figure>
{% endtab %}

{% tab title="Info" %}

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2Fh8XLVHGoyPH020ZaniXv%2Finfo.png?alt=media&amp;token=9716852f-40ca-404b-9540-884e78245a88" alt=""><figcaption><p>Info notification</p></figcaption></figure>
{% endtab %}

{% tab title="Achievement" %}

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2F0ZgGD53wXjfMmzxBLYgF%2Fachievement.png?alt=media&amp;token=c8ba19b4-24c5-4b49-8d9a-f0e5d10dce49" alt=""><figcaption><p>Achievement notification</p></figcaption></figure>
{% endtab %}
{% endtabs %}

***

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

Quick installation guide for pc\_notifications

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however basic knowledge of lua and sceipts configuration is very useful.
{% endhint %}

{% hint style="info" %}
**Framework & dependencies info**

This script is standalone and doesn't require any other resources to work.
{% endhint %}

## Installing the resource

{% stepper %}
{% step %}

### Add the resource to your server

Upload the file into your resources folder

Add the script by adding following to the server.cfg: *`ensure pc_notifications`*
{% endstep %}

{% step %}

### Integrate into existing resources

Use exports and events in other resources to utilize pc\_skills functions. More info in [usage tab](/scripts/pc_notifications/usage)
{% endstep %}

{% step %}

### Restart your server

Restart the server for the change to take effect. Monitor the console and check for any errors
{% endstep %}
{% endstepper %}


# Usage

Quick usage tutorial

{% hint style="info" %}
**Each notification call should consist of following:**

* Type - one of 5 notifications types *(see Features)*
* Title - notification title
* Text - notification text
* Time - how long should the notification stay on screen (in miliseconds)
* Sound - should the sound be played (true/false)
  {% endhint %}

## There are two ways of sending a notification:

### By event:

#### *Triggering on client side:*

<pre class="language-lua"><code class="lang-lua"><strong>TriggerEvent('pc_notifications:Notify', type, title, text, time, true) 
</strong></code></pre>

#### *Triggering on server side:*

```lua
TriggerClientEvent('pc_notifications:Notify', playerId, type, title, text, time, true) 
```

### By export:

```lua
exports['pc_notifications']:Notify(type, title, text, time, true)
```


# Copy of pc\_collections

Let your players exchange sets of items for rewards and xp.

## Video preview:

{% embed url="<https://youtu.be/U6JvJbEpvAg>" %}
Script showcase
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Overview of pc\_collections features

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Dependencies & integrations

Dependencies & integrations of this resource

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however to get the most out of it, basic knowledge of lua and scripts configuration is very useful.
{% endhint %}

## Dependencies (mandatory)

One of supported frameworks

{% hint style="info" %}
*"<mark style="color:green;">Recommended version</mark>" only tells you what version of the resource has been tested, other versions (including older ones) will likely work as well.*
{% endhint %}

|                            Framework                            |                                                         Recommended version                                                        |
| :-------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |
|         [ESX](https://github.com/esx-framework/esx_core)        |                                                           1.6.0 or newer                                                           |
| [QBCore](https://github.com/qbcore-framework/qb-core/tree/main) |                                                           1.2.6 or newer                                                           |
|         [QBox](https://github.com/Qbox-project/qbx_core)        |                                                           1.22.3 or newer                                                          |
|   [ox\_core](https://github.com/overextended/ox_core/releases)  |                                                           1.2.2 or newer                                                           |
|                              custom                             | You can add a custom framework integration or ask us to do it for you. We do not guarantee compatibility with any custom framework |

{% hint style="warning" %}
If you want to use another framework, you can ask us to provide support for it or you can add it yourself using our bridge module if you know what you're doing.
{% endhint %}

## Integrations (optional)

{% hint style="info" %}
*"<mark style="color:green;">Recommended version</mark>" only tells you what version of the resource has been tested, other versions (including older ones) will likely work as well.*
{% endhint %}

<table><thead><tr><th align="center" valign="middle">Resource</th><th align="center">Recommended version</th><th align="center">Description</th></tr></thead><tbody><tr><td align="center" valign="middle"><a href="https://github.com/overextended/oxmysql">oxmysql</a></td><td align="center">2.12.0 or newer</td><td align="center">Our resource requires data storage, we recommend oxmysql, but if you don't want to use it data can be saved in a JSON file on the server side.</td></tr><tr><td align="center" valign="middle"><a href="https://github.com/overextended/ox_inventory/releases">ox_inventory</a></td><td align="center">2.38.1 or newer</td><td align="center">If you have ox_inventory, item icons will be downloaded automatically, if not you'll have to add them manually, see <a href="/scripts/pc_collections/tutorials">tutorials</a></td></tr><tr><td align="center" valign="middle"><a href="https://tebex.poisoncode.com/scripts/skills">pc_skills</a></td><td align="center">1.1</td><td align="center"> Using pc_skills is recommended for the best functionality. It gives an option to lock some collections until player reches given level, and awarding xp for claiming collections</td></tr><tr><td align="center" valign="middle"><a href="https://tebex.poisoncode.com/scripts/">pc_notifications</a></td><td align="center">1.0</td><td align="center">pc_notifications integration is built-in and it can be used for notifications. Remember to enable it in the config</td></tr><tr><td align="center" valign="middle">pc_ui (not released yet)</td><td align="center">(not released yet)</td><td align="center">pc_ui integration is built-in and it can be used for notifications. Remember to enable it in the config</td></tr><tr><td align="center" valign="middle"><a href="https://github.com/CommunityOx/ox_lib">ox_lib</a></td><td align="center">3.30.6 or newer</td><td align="center">ox_lib integration is built-in and it can be used for notifications.. Remember to uncomment it in the fxmanifest.lua and enable it in the config</td></tr></tbody></table>


# Installation

### Installing the resource

{% stepper %}
{% step %}

### Add the resource to your server

Upload the file into your resources folder

Load `database.sql` into your database <mark style="color:blue;">(If using oxmysql)</mark>

Add the script by adding following to the server.cfg: *`ensure pc_collections`*
{% endstep %}

{% step %}

### Configure the resource

Check all the config and editable files to adjust the resource to your needs
{% endstep %}

{% step %}

### Restart your server

Restart the server for the change to take effect, check for any errors and test out the resource
{% endstep %}
{% endstepper %}


# Editable files preview

Config and bridge files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is customizable, you can preview files you have access to below.
{% endhint %}

## Main files

{% hint style="info" %}
These are the main files you can edit to customize this resource
{% endhint %}

<details>

<summary>client/cl_editable.lua</summary>

```lua
---------------------------------------------------------------------------------
--   _____       _                    _____          _                         --
--  |  __ \     (_)                  / ____|        | |                        --
--  | |__) |__   _  ___   ___   _ __ | |      ___   __| |  ___                 --
--  |  ___/ _ \ | |/ __| / _ \ | '_ \| |     / _ \ / _` | / _ \                --
--  | |  | (_) || |\__ \| (_) || | | | |____| (_) | (_| ||  __/                --
--  |_|   \___/ |_||___/ \___/ |_| |_|\_____|\___/ \__,_| \___|                --
--                                                                             --
--  © PoisonCode                                                               --
---------------------------------------------------------------------------------
--  Discord:       https://discord.gg/rNJ8cHXCsN                               --
--  Tebex:         https://tebex.poisoncode.com                                --
--  Docs:          https://docs.poisoncode.com                                 --
---------------------------------------------------------------------------------


RegisterCommand("collections", function(source, args)
    showUI(not IsUiDisplayed)
end)


exports('openCollections', function()
    showUI(not IsUiDisplayed)
end)


RegisterNetEvent('pc_collections:client:openCollections', function()
    showUI(not IsUiDisplayed)
end)
```

</details>

<details>

<summary>server/sv_editable.lua</summary>

```lua
COLLECTIONS_CACHE = {}
Database = {}

---------------------------------------------------------------------------------
--   _____       _                    _____          _                         --
--  |  __ \     (_)                  / ____|        | |                        --
--  | |__) |__   _  ___   ___   _ __ | |      ___   __| |  ___                 --
--  |  ___/ _ \ | |/ __| / _ \ | '_ \| |     / _ \ / _` | / _ \                --
--  | |  | (_) || |\__ \| (_) || | | | |____| (_) | (_| ||  __/                --
--  |_|   \___/ |_||___/ \___/ |_| |_|\_____|\___/ \__,_| \___|                --
--                                                                             --
--  © PoisonCode                                                               --
---------------------------------------------------------------------------------
--  Discord:       https://discord.gg/rNJ8cHXCsN                               --
--  Tebex:         https://tebex.poisoncode.com                                --
--  Docs:          https://docs.poisoncode.com                                 --
---------------------------------------------------------------------------------

-- ====================================================================================================== --
-- ------------------------------------------- DATABASE ------------------------------------------------- --
-- ====================================================================================================== --

function Database.Init()
    if Config.Database == "oxmysql" and GetResourceState('oxmysql') ~= 'started' then
        print("^1[ERROR] oxmysql defined in config but not started. Switching to JSON.^0")
        Config.Database = "json"
    end

    if Config.Database == "json" then
        local loadFile = LoadResourceFile(GetCurrentResourceName(), "./server/data.json")
        if loadFile then
            COLLECTIONS_CACHE = json.decode(loadFile)
        else
            COLLECTIONS_CACHE = {}
        end
    end
end


---@param PID string Player Unique Identifier
---@param collectionName string|nil Collection name to get count for
---@return number|table 
function Database.GetLimitCounts(PID, collectionName)
    if Config.Database == "oxmysql" then
        if not collectionName then
            local result = {}
            local response = MySQL.prepare.await('SELECT collection, count FROM `collections` WHERE `player_id` = ?', {PID})
            if response then
                if #response == 0 and response.collection then response = {response} end
                for _, row in ipairs(response) do
                    result[row.collection] = row.count
                end
            end
            return result
        else
            return MySQL.prepare.await('SELECT count FROM `collections` WHERE `player_id` = ? AND `collection` = ?', {PID, collectionName}) or 0
        end
    else
        if not COLLECTIONS_CACHE[PID] then return (collectionName and 0 or {}) end
        if collectionName then
            return COLLECTIONS_CACHE[PID][collectionName] or 0
        else
            return COLLECTIONS_CACHE[PID]
        end
    end
end


---@param PID string Player Unique Identifier
---@param collectionName string
function Database.IncrementLimit(PID, collectionName)
    if Config.Database == "oxmysql" then
        MySQL.prepare.await('INSERT INTO collections (player_id, collection, count) VALUES (?,?,1) ON DUPLICATE KEY UPDATE count = count + 1', {PID, collectionName})
    else
        if not COLLECTIONS_CACHE[PID] then COLLECTIONS_CACHE[PID] = {} end
        COLLECTIONS_CACHE[PID][collectionName] = (COLLECTIONS_CACHE[PID][collectionName] or 0) + 1
        SaveResourceFile(GetCurrentResourceName(), "./server/source/data.json", json.encode(COLLECTIONS_CACHE), -1)
    end
end

-- ====================================================================================================== --
-- --------------------------------------------- OTHER -------------------------------------------------- --
-- ====================================================================================================== --

---@param player player Player source id
---@return string Unique Player identifier
function GetPlayerUID(player)
    if Config.Framework == "ESX" then
        return Bridge.GetPlayer(player).identifier
    elseif Config.Framework == "QBCore" or Config.Framework == "QBox" then
        return Bridge.GetPlayer(player).PlayerData.citizenid
    elseif Config.Framework == "ox_core" then
        return Bridge.GetPlayer(player).charId
    end
end


RegisterNetEvent('pc_collections:server:openCollections', function(target)
    TriggerClientEvent('pc_collections:client:openCollections', target)
end)


exports('openCollections', function(target)
    TriggerClientEvent('pc_collections:client:openCollections', target)
end)
```

</details>

<details>

<summary>config.lua</summary>

```lua
Config = {}
Locales = {}
Bridge = {}
---------------------------------------------------------------------------------
--   _____       _                    _____          _                         --
--  |  __ \     (_)                  / ____|        | |                        --
--  | |__) |__   _  ___   ___   _ __ | |      ___   __| |  ___                 --
--  |  ___/ _ \ | |/ __| / _ \ | '_ \| |     / _ \ / _` | / _ \                --
--  | |  | (_) || |\__ \| (_) || | | | |____| (_) | (_| ||  __/                --
--  |_|   \___/ |_||___/ \___/ |_| |_|\_____|\___/ \__,_| \___|                --
--                                                                             --
--  © PoisonCode                                                               --
---------------------------------------------------------------------------------
--  Discord:       https://discord.gg/rNJ8cHXCsN                               --
--  Tebex:         https://tebex.poisoncode.com                                --
--  Docs:          https://docs.poisoncode.com                                 --
---------------------------------------------------------------------------------

-- ====================================================================================================== --
-- ---------------------------------------- BRIDGE SETTINGS --------------------------------------------- --
-- ====================================================================================================== --

Config.Framework = "ESX" -- "ESX" / "QBCore" / "QBox" / "ox_core"
Config.InventorySystem = "ox_inventory" -- "ox_inventory" / "ESX" / "QBCore" / "QBox" (ox_core has to use ox_inventory)
Config.Database = "oxmysql" -- "oxmysql" / "json" -- we recommend using oxmysql, you have to setup the database table using the provided SQL file.
Config.Notifications = "pc_notifications" -- pc_notifications / "custom"

-- ====================================================================================================== --
-- -------------------------------------------- GENERAL ------------------------------------------------- --
-- ====================================================================================================== --

Config.Locale = "en" -- Set your language here -- available languages: en 

Config.Debug = false -- Enable debug messages

Config.ShowClaimLimits = true -- Show claim limits in the UI
Config.ShowRewardIcons = true -- Show reward icons in the UI, if disabled only names will be shown

-- ====================================================================================================== --
-- -------------------------------------------- SKILLS -------------------------------------------------- --
-- ====================================================================================================== --
--   For this section to work, you need pc_skills resource: https://tebex.poisoncode.com/scripts/skills   --
-- ====================================================================================================== --

Config.UseSkillSystem = true -- Enable skill system integration
Config.SkillName = "skill1" -- Skill name from the skill system 

-- ====================================================================================================== --
-- ------------------------------------------ COLLECTIONS ----------------------------------------------- --
-- ====================================================================================================== --

Config.Collections = {

    ["collection1"] = { -- Collection name
        order = 1, -- Order in the list
        label = "Example collection 1", -- Collection name
        limit = false, -- Limit how many times player can complete this collection, set to false for unlimited
        items = { -- Items required to complete this collection
            {name = "bread", label = "Bread", count = 2},
        },
        reward = { -- Items rewarded for completing this collection
            {name = "money", label = "Cash", count = 500},
        },
        -- pc_skills integration, this will only work if Config.pc_skills.enabled is set to true
        lvl = 1, -- Level required to unlock this collection, only works if Config.pc_skills.enabled is set to true
        rewardXP = 200, -- Experience points rewarded for completing this collection, only works if Config.pc_skills.enabled is set to true
    },

    ["collection2"] = {
        order = 2,
        label = "Example collection 2",
        limit = 1,
        items = {
            {name = "weed", label = "Weed", count = 5},
            {name = "glove", label = "Gloves", count = 2},
            {name = "mask1", label = "Mask", count = 1},
        },
        reward = {
            {name = "armour", label = "Bulletproof vest", count = 1},
            {name = "weapon_pistol", label = "Pistol", count = 1},
        },
        lvl = 1,
        rewardXP = 10,
    },

    ["collection3"] = {
        order = 3,
        label = "Example collection 3",
        limit = 1,
        items = {
            {name = "carrot", label = "Carrot", count = 1},
            {name = "eggplant", label = "Eggplant", count = 1},
            {name = "Pepper", label = "Pepper", count = 1},
        },
        reward = {
            {name = "money", label = "Cash", count = 20000},
        },
        lvl = 5,
        rewardXP = 20,
    },

    ["collection4"] = {
        order = 4,
        label = "Example collection 4",
        limit = 2,
        items = {
            {name = "bread", label = "Bread", count = 1},
            {name = "bread", label = "Bread", count = 1},
            {name = "bread", label = "Bread", count = 1},
            {name = "bread", label = "Bread", count = 1},
            {name = "bread", label = "Bread", count = 1},
            {name = "water", label = "Water", count = 1},
            {name = "water", label = "Water", count = 1},
            {name = "water", label = "Water", count = 1},
            {name = "water", label = "Water", count = 1},
            {name = "water", label = "Water", count = 1},
        },
        reward = {
            {name = "usb_black", label = "USB stick", count = 2},
        },
        lvl = 3,
        rewardXP = 20,
    },

        ["collection5"] = {
        order = 5,
        label = "Example collection 5",
        limit = 3,
        items = {
            {name = "advancedkit", label = "Repair kit", count = 1},
            {name = "ziptie", label = "Ziptie", count = 5},
        },
        reward = {
            {name = "internals", label = "Parts", count = 2},
        },
        lvl = 3,
        rewardXP = 100,
    },
    
}
```

</details>

## Bridge module

{% hint style="info" %}
Bridge module is used to provide compatibility with different frameworks, inventory systems and much more, you can see which system are supported and if you have sufficient knowledge - add a new one.
{% endhint %}

<details>

<summary>server/framework.lua</summary>

```lua
if Config.Framework ~= "ESX" or Framework then return end

Framework = exports["es_extended"]:getSharedObject() or nil
if not Framework then
  TriggerEvent('esx:getSharedObject', function(library) 
    Framework = library or nil
  end)
end

Bridge.GetPlayer = Framework.GetPlayerFromId

if Framework and Bridge.GetPlayer then
  print("Loaded ESX framework")
end
```

</details>

<details>

<summary>server/inventory.lua</summary>

```lua
if Config.Framework ~= "ox_core" or Framework then return end

if not Ox then
    print("Ox_Core framework not found, make sure it's uncommented in fxmanifest.")
end

Bridge.GetPlayer = Ox.GetPlayerFromUserId

if not Config.InventorySystem == "ox_inventory" then
    print("You cannot use ox_core framework without ox_inventory, please check your Configuration.")
    Config.InventorySystem = "ox_inventory"
end

if Ox and Bridge.GetPlayer and Config.InventorySystem == "ox_inventory" then
    print("Loaded ox_core framework")
end

```

</details>

<details>

<summary>server/notifications.lua</summary>

```lua
if Config.Framework ~= "QBCore" or Framework then return end

Framework = exports["qb-core"]:GetCoreObject() or nil

Bridge.GetPlayer = Framework.Functions.GetPlayer

if Framework and Bridge.GetPlayer then
    print("Loaded QBCore framework")
end
```

</details>

{% hint style="info" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Events, exports & commands

A list of events, exports and commands used by this resource with examples

## **Client side**

### Events

<details>

<summary>Opening the collections menu</summary>

```lua
TriggerEvent('pc_collections:client:openCollections')
```

{% hint style="info" %}
Opens the collection menu for the player that triggered the event.
{% endhint %}

</details>

### Exports

<details>

<summary>Opening the collections menu</summary>

```lua
exports.pc_collections:openCollections()
```

{% hint style="info" %}
Opens the collection menu for the player that triggered the export.
{% endhint %}

</details>

### Commands

<details>

<summary>Opening the collections menu</summary>

```lua
/collections
```

{% hint style="info" %}
Opens the collection menu for the player that triggered the command.
{% endhint %}

{% hint style="warning" %}
This command is avaiable for everyone by default.
{% endhint %}

</details>

***

## **Server side**

### Events

<details>

<summary>Opening the collections menu</summary>

```lua
TriggerEvent('pc_skills:server:openCollections', target)
or
TriggerClientEvent('pc_collections:client:openCollections', target)
```

* **target** - player server id - id of the player you want to open menu for

{% hint style="info" %}
Opens the menu for the player with specified id
{% endhint %}

</details>

### Exports

<details>

<summary>Opening the collections menu</summary>

```lua
exports.pc_collections:openCollections(target)
```

* **target** - player server id - id of the player you want to open menu for

{% hint style="info" %}
Opens the menu for the player with specified id
{% endhint %}

</details>


# Tutorials

Tutorials that might help you with customization or setting up the resource

<details>

<summary>Using icons without ox_inventory</summary>

{% hint style="info" %}
When using ox\_invenotry, the icons are set automatically. However if your server doesn't use ox\_inventory you'll have to upload the icons manually.
{% endhint %}

1. Prepare your images (in a .png format)
2. Open `pc_collections/web/img`
3. Drop your images into this folder
4. Use `refresh` and `restart pc_collections` in your server console
5. Verify that icons show up in the UI

Youtube tutorial: (soon!)

</details>


# Updates

Page dedicated to update trailers and hisotry.

## Update history

<details>

<summary>Update 1.1</summary>

• Entirely new UI style

• Hovering over items shows tooltips with their names and amount

• Rewards can now be showed as icons as well

• Internal optimization

• New, more organised file structure

• Major update of the docs page

</details>


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


# pc\_airdrop

Documentation page for pc\_airdrop

## Choose a version

{% content-ref url="/pages/rTddNvfibmah01y2Dpyw" %}
[pc\_airdrop 2.0](/group-1/pc_airdrop/pc_airdrop-2.0)
{% endcontent-ref %}

{% content-ref url="/pages/Cxc8sRBQwM20XZRlsFGm" %}
[pc\_airdrop legacy](/group-1/pc_airdrop/pc_airdrop-legacy)
{% endcontent-ref %}

{% hint style="success" %}
When you buy this resource you get both 2.0 and legacy version - you decide which one you'd like to use.
{% endhint %}


# pc\_airdrop 2.0

Refreshed & improved airdrops script featuring deep customization and new functions.

## Video preview:

{% embed url="<https://www.youtube.com/watch?v=stl7tziz4eg>" %}
*See this script in action!*
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Features of pc\_airdrop 2.0

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

How to install pc\_airdrop 2.0

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however to get the most out of it, basic knowledge of lua and scripts configuration is very useful.
{% endhint %}

**1.** Upload the file into your resources folder\
\
**2.** Start the script, by adding this to your server.cfg:\
*`ensure pc_airdrop`*


# Open files preview

Config & Utils files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is highly customizable, there is a total of 4 files accessible for you.
{% endhint %}

## config/config.lua

<details>

<summary>Click here to see a preview config.lua</summary>

```lua
```

</details>

## config/translations.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
```

</details>

## client/utils.lua

<details>

<summary>Click here to see a preview client.lua</summary>

```lua
```

</details>

## server/utils.lua

<details>

<summary>Click here to see a preview server.lua</summary>

```lua
```

</details>

{% hint style="danger" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Tutorials

These tutorials will walk you through all the options available.

## Config.lua tutorial:

{% embed url="<https://www.youtube.com/watch?v=iC7wfVIm5KE>" %}
Config tutorial
{% endembed %}

## Other tutorials:

<details>

<summary>How to enable command only for admins (ESX)</summary>

Modify existing command code in `server/utils.lua` to look like this:

```lua
-- COMMAND
-- WARNING: THIS COMMAND IS AVAILABLE FOR EVERYONE BY DEFAULT, CHANGE THE CODE BELOW TO ONLY ALLOW CERATIN PEOPLE TO CALL THE AIRDROP!
if Config.AirdropSpawnMethod['command_enabled'] then
    RegisterCommand("airdrop", function(source, args, rawCommand)
        local xPlayer = Framework.GetPlayerFromId(source)
        if (xPlayer.group == 'superadmin' or xPlayer.group == 'admin' or xPlayer.group == 'mod') then
            createNewAirdrop({type = "command", src = source, arg = args})
        end
    end)
end
```

You can add/remove groups as required

</details>

<details>

<summary>How to enable command only for admins (QBcore/Qbox)</summary>

Soon

</details>

<details>

<summary>How to synchronize airdrops for players joining the server</summary>

Soon

</details>

<details>

<summary>How to spawn airdrop using arguments (choose place and type)</summary>

Soon

</details>


# Updates

Page dedicated to update trailers and hisotry.

### Trailers

{% embed url="<https://youtu.be/vDc8Xc22ZJE>" %}
***1.2 update trailer***
{% endembed %}

### Update history

<details>

<summary>Update 1.3.9</summary>

* Bug fix

</details>


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


# pc\_airdrop legacy

This is one of the most advanced scripts for the airdrop system with great performance. It can be configured in many different ways and used in lots game modes. Check it out yourself!

## Video preview:

{% embed url="<https://www.youtube.com/watch?v=stl7tziz4eg>" %}
*See this script in action!*
{% endembed %}

## Available at: [tebex.poisoncode.com](https://tebex.poisoncode.com)


# Features

Overview of pc\_airdrop features

<table data-view="cards" data-full-width="true"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>High customizability</strong></em><strong> ⚙️</strong></td><td>You can easily adjust this resource to your needs! (click this card to view config file preview)</td><td><a href="/scripts/pc_airdrop/open-files-preview">Open files preview</a></td></tr><tr><td><em><strong>High performance</strong></em><strong> 💪</strong></td><td>This asset was built with high performance in mind - It's unnoticeable when idle and very efficient when being used!</td><td></td></tr><tr><td><em><strong>Discord notifications</strong></em><strong> 🤖</strong></td><td>This script comes with discord webhook notifications built in!<br>All you need to do is add your own webhook url and you're good to go!</td><td><a href="/pages/Veg2Rkm32HVnpij6OcQc#server_utils.lua">/pages/Veg2Rkm32HVnpij6OcQc#server_utils.lua</a></td></tr></tbody></table>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><em><strong>Be a part of our community</strong></em> 😎</td><td>When you buy our product you not only become our customer, but a part of our community. We strive for the best possible customer experience, if you have any suggestions regarding existing resources or ideas for future relases -  don't hesitate to contact us, we'll be happy to hear your thoughts!</td><td></td><td></td></tr><tr><td><em><strong>Active support team</strong></em><strong> ⚒️</strong></td><td>When you purchase a resource from our store, you'll be granted a special role on our discord, giving you access to quick support in case you encounter any problems/bugs with our script.</td><td></td><td><a href="https://discord.gg/rNJ8cHXCsN/">https://discord.gg/rNJ8cHXCsN/</a></td></tr></tbody></table>

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FD9hCJmThPSLdpCPd42z2%2F3.png?alt=media&amp;token=fa8a8f59-108c-4a48-b5a6-feb2709cd36c" alt=""><figcaption><p><em>Airdrop in the sky and flare on the landing spot</em></p></figcaption></figure>

***

<figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FELpOp16d0JYw2VGMqfGI%2F2.png?alt=media&amp;token=1812fd4d-9d16-4c56-8510-6251e524a4f6" alt=""><figcaption><p><em>Airdrop after landing</em></p></figcaption></figure>

***

<div><figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FovZQGZYdIZ6STocum1k0%2F1.png?alt=media&amp;token=d997b283-6eec-4a79-b118-94310c8b143f" alt="" width="349"><figcaption><p><em>Discord webhook notification 1</em></p></figcaption></figure> <figure><img src="https://2573349196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9dPvlaTnim1vYP4KfTQO%2Fuploads%2FaL9zm8byT4hIYTBuBDKI%2F4.png?alt=media&amp;token=fd61b740-a5a0-4baa-8b57-76f72ca52e6d" alt=""><figcaption><p><em>Discord webhook notification 2</em></p></figcaption></figure></div>

{% hint style="warning" %}
This resource is protected by Fivems Asset Escrow system.\
Learn more: <https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151>
{% endhint %}


# Installation & dependencies

Quick installation guide for pc\_airdrop

{% hint style="success" %}
**Beginner friendly**

You don't need to be an experienced developer to use this resource, however to get the most out of it, basic knowledge of lua and scripts configuration is very useful.
{% endhint %}

**1.** Upload the file into your resources folder\
\
**2.** Start the script, by adding this to your server.cfg:\
*`ensure pc_airdrop`*


# Open files preview

Config & Utils files preview, these files are ignored by the escrow system and can be modified.

{% hint style="success" %}
This script is highly customizable, there is a total of 4 files accessible for you.
{% endhint %}

## config/config.lua

<details>

<summary>Click here to see a preview config.lua</summary>

```lua
Config = {} 
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io


----- This script is highly customizable, plase take your time and adjust it to your needs! ------
--------------------------------------------------------------------------------------------------
--  --------------- If you encounter any bugs, please report it on our discord ---------------- --
-- Documentation available at: https://poisoncode.gitbook.io/poisoncode-docs/scripts/pc_airdrop --
--------------------------------------------------------------------------------------------------

-- BY DEFAULT REWARD ITEMS ARE GIVEN USING ESX OR QBCORE FUNCTION. 
-- SEE utils/server.lua TO MODIFY FUNCTION RESPONSIBLE FOR ADDING ITEMS TO INVENTORY!

Config.UseExperimentalFeatures = false -- If you want to use experimental features, set this to true. (Experimental features are not fully tested and may not work as expected)

Config.ClientDebugMessages = false -- Enables additional debug messages on the client side console (F8), we recommend turning this off while not debugging 
Config.ServerDebugMessages = true -- Enables additional debug messages on the server side console, you can keep it on if these messages don't bother you
-- ^^ (Errors and warnings will always be displayed) ^^


--   Technical  --
Config.Framework = "NEW-ESX"      -- "NEW-ESX" - "ESX" - "QBCore"
Config.UseCollision2 = false       -- If you're experiencing an error where the airdrop gets stuck in the air, set this to true, otherwise it's not recommended


--    Basic     --
Config.CaptureTime = 15             -- Time (in seconds) required to capture an airdrop
Config.CaptureDistance = 10         -- How far can you go before airdrop capture is canceled
Config.AirdropFallSpeed = 1       -- How quick should the airdrop fall. Default 1 (Slowest setting) (Used only when Config.AirdropGroundSpawn is false)
Config.AirdropGroundSpawn = false   -- Should the airdrop spawn directly on the ground (Without falling down from the sky)
Config.LocationNotification = true  -- If set to true, notification will contain airdrop name and location name


-- Discord logs --
Config.DiscordLogs = false         -- Set to true if you want to recieve discord logs. (set your webhook in server utils)


-- Integrations --
Config.Use_Target = false           -- If you set this option to true, collecting the airdrop will be possible only using qTarget or ox_target
Config.Target = "ox_target"         -- "qtarget" or "ox_target" or "qb-target"
Config.Use_OxInventory = false      -- Turns on ox_inventory integration
Config.RemoveWhenEmpty = false      -- When using ox_inventory, should the airdrop crate be removed when players take all the items
Config.Use_pcnotifications = GetResourceState('pc_notifications') == 'started' or false -- Automatically turns the pc_notification on or off


--  Advanced    --
Config.Plane = {
    enable = true,                 -- Should there be a plane flying by and dropping the airdrop
    distance = 500,                -- How far should the plane spawn (bigger value = longer fly time)
    speed = 50,                    -- How fast should the plane travel
}


Config.AirdropRemove = {
    enable = true,                 -- Removes the airdrop after a period of time. Especially recommended if you're using ox_inventory integration
    time = 1200,                   -- Time (in seconds) after the airdrop is automatically removed from the map (example 1200 sec = 20 min)
}


Config.AirdropLock = {
    enable = true,                 -- After falling/spawning, should the airdrop be locked
    showtext = true,               -- Should there be 3D/Helpnotification/custom text showed when the airdrop is locked AirdropLock.enable should be set to true
    time = 15,                     -- How long should the airdrop be locked for after falling/spawning
}


Config.Models = {
    -- You can change crate and/or parachute prop model. List of all props: https://gta-objects.xyz/objects/
    -- Warning: some models might not work or have different physics. Change at your own risk
    crate = "xm_prop_rsply_crate04a",
    parachute = "p_parachute1_mp_dec",

    -- Plane model
    plane = "titan",

    -- Ped model for pilot. List of all peds: https://docs.fivem.net/docs/game-references/ped-models/ 
    npc_pilot = "s_m_m_pilot_02",
}


Config.ShowFlares = {
    [1] = true, -- Should there be a "flare effect" on the ground when the airdrop is falling ? (true/false)
    [2] = true, -- Should there be a "flare + smoke effect" on the airdrop crate when it lands on the ground ? (true/false)
}


-- Basic methods of spawning an airdrop
Config.AirdropSpawnMethod = {
    event_enabled = false, -- Creates a custom server event for calling an airdrop (customize in utils/server.lua)

    command_enabled = true, -- Should you be able to call command /airdrop (customize in utils/server.lua) WARNING: BY DEFAULT COMMAND IS AVAILABLE FOR EVERYONE

    auto_enabled = false, -- Should there be an airdrop every X minutes (Set cooldown below)
    auto_delay = 10, -- After server/script restart, first airdrop will be called in after this delay (in minutes) DEFAULT: 10 MINUTE DELAY
    auto_timer = 60, -- Time in minutes between airdrops. DEFAULT: AIRDROP EVERY 60 MINUTES
}


-- If you want your airdrop to be called in a random spot within provided coords and radius set Config.AirDropInRadius to true.
-- If you set Config.AirDropInRadius to false, the airdrop will always land on provided coords, and radius will be ignored (we suggest to disable radius blip if in that case).
-- Airdrop location will be choosen at random from coords provided below, unless a forced location will be provided with a command or through event.
-- Bear in mind that this script does not check if location is safe, it is possible that the airdrop falls in water. Be cautious when setting spawn radius
Config.AirDropInRadius = true 
Config.Locations = {
    {label = "Location1", coords = vector3(3678.45, 4983.75, 15.83), radius = 50},
    {label = "Location2", coords = vector3(1450.03, 4408.11, 46.77), radius = 50},
    {label = "Location3", coords = vector3(112.3445, 6827.7622, 17.0759), radius = 80},
    {label = "Location4", coords = vector3(2504.6265, 3264.5303, 52.1214), radius = 50},
    {label = "Location5", coords = vector3(1573.6575, 3196.3611, 40.5308), radius = 70},
    {label = "Location6", coords = vector3(334.0565, 2849.8423, 43.4093), radius = 60},
    {label = "Location7", coords = vector3(-2171.0454, 3063.4260, 32.9654), radius = 60},
    {label = "Location8", coords = vector3(-1334.5094, -3040.9614, 13.9444), radius = 100},
    {label = "Location9", coords = vector3(1373.5991, -745.4987, 67.2328), radius = 60},
}


-- Configure airdrops and their loot, type of spawned airdrop is random.
-- ItemName is the name used to give the item to the player.
-- ItemAmount OR ItemAmountMin & ItemAmountMax is the amount of this item that should be awarded to player.
-- If using ItemAmountMin & ItemAmountMax the amount will be chosen at random between these two values
-- Chance is the chance (1-100) that the player recieves this item. If set to 100, player will always recieve this reward.
-- Type "item" or "weapon" (will be ignored if using ox_inventory), used when you DONT use "weapons as items" (you can only have one weapon of given type for example).
Config.AirDrops = {
    [1] = { -- Airdrop id number (has to bee different for each airdrop)
        ['name'] = "Medical", -- Name of the airdrop that will be shown in notifications
        ['items'] = { -- Loot table
            {ItemName = "bandage", ItemAmountMin = 5, ItemAmountMax = 10, Chance = 100, Type = "item"}, -- items, amount and chance
            {ItemName = "medikit", ItemAmount = 1, Chance = 90, Type = "item"},
        },
        },
    [2] = {
        ['name'] = "Weapons", -- Name of the airdrop that will be shown in notifications
        ['items'] = { -- Loot table
            {ItemName = "weapon_pistol", ItemAmount = 1, Chance = 100, Type = "item"}, -- <- if not using ox_inventory or other "weapons as items" script, change type to "weapon"
            {ItemName = "weapon_pistol50", ItemAmount = 1, Chance = 10, Type = "item"},
        },
        },
    [3] = {
        ['name'] = "Drugs", -- Name of the airdrop that will be shown in notifications
        ['items'] = { -- Loot table
            {ItemName = "weed", ItemAmount = 10, Chance = 100, Type = "item"},
            {ItemName = "coke", ItemAmountMin = 5, ItemAmountMax = 15, Chance = 50, Type = "item"},
        },
    },
}


-- List of blip IDs and colors - https://docs.fivem.net/docs/game-references/blips/
Config.blips = {
    main = { 
    ['enabled'] = true, -- Should the main blip be on ? (true/false)
    ['scale'] = 1.2, -- Scale of the main blip
    ['blipId'] = 568, -- Blip (See link above for reference)
    ['color'] = 81, -- Blip color (See link above for reference)
    ['type'] = 6, -- For blip types, please reference: https://docs.fivem.net/natives/?_0x9029B2F3DA924928
    ['name'] = "Airdrop", -- Label of the main blip on the map
    },

    radius = {
    ['enabled'] = true, -- Should the radius blip be on ? (true/false)
    ['color'] = 81, -- Radius blip color (See link above for reference)
    ['alpha'] = 100, -- Radius blip transparency (higher number = lower transparency)
    },
}


Config.Marker = {
    enabled = true, -- Should there be a circle marker showing how far can you go when capturing an airdrop
    red = 0, green = 125, blue = 255, -- You can customize the color of the marker by adjusting RGB (0-255)
}


-- How should "collect" text be showed. Available types:
-- 0 - No 3D Text or notification will be displayed
-- 1 - 3D text is being displayed on the airdrop crate (This option is slightly less efficient, if you care about performance use another type)
-- 2 - Help notofication is being displayed in the top left corner.
-- 3 - Will run code from CustomCaptureFuntion in utils/client.lua.
-- See https://poisoncode.gitbook.io/poisoncode-docs/scripts/pc_airdrop for reference
Config.captureTextType =  1


-- [[Notifications functions has been moved to: utils/client.lua]]
-- [[Custom event has been moved to utils/client.lua]]
```

</details>

## config/translations.lua

<details>

<summary>Click here to see a preview translations.lua</summary>

```lua
-- _____                          _____          _      
-- |  __ \    (_)                / ____|        | |     
-- | |__) |__  _ ___  ___  _ __ | |     ___   __| | ___ 
-- |  ___/ _ \| / __|/ _ \| '_ \| |    / _ \ / _` |/ _ \
-- | |  | (_) | \__ \ (_) | | | | |___| (_) | (_| |  __/
-- |_|   \___/|_|___/\___/|_| |_|\_____\___/ \__,_|\___|
-- © PoisonCode 
-- Discord: https://discord.gg/rNJ8cHXCsN
-- Tebex: https://poisoncode.tebex.io
 
Translations = {
    base = {
        -- Text showed during airdrop capture
        ["collect"] = "Press ~y~E ~w~to capture the airdrop", -- Text shown before player started capturing the airdrop
        ["collecting"] = "Airdrop is being captured. Remaining: ~b~%s ~w~sec",

        -- Used only when Config.AirdropGroundTextLabel is true
        ["airdrop_locked"] = "~r~Airdrop locked for: ~y~%s ~w~sec",

        -- Notifications text
        ["notification_inbound"] = "Airdrop has been called!",
        ["notification_inbound_2"] = "%s airdrop was called to %s!",
        ["notification_startcapture"] = "You are capturing the airdrop!",
        ["notification_captured"] = "You captured the airdrop!",
        ["notification_capturecanceled"] = "You cancelled airdrop capture!",
        ["notification_someonecaptured"] = "Someone captured the airdrop!",
        ["notification_cantcapture"] = "This airdrop is already being captured!",

        -- Target event label
        ["TargetText"] = "Capture the airdrop", 

        -- Ox_inventory stash label
        ["oxinv_label"] = "Airdrop",
    },

    -- Discord logs
    discord = {
        -- Titles
        ["title_auto"] = "Airdrop called automatically",
        ["title_command"] = "Airdrop called by command",
        ["title_event"] = "Airdrop called by event",
        ["title_captured"] = "Airdrop captured",
        ["title_remove"] = "Airdrop removed",

        -- Desc
        ["call_auto"] = "Airdrop was called in automatically!",
        ["call_command"] = "called an airdrop using a command!",
        ["call_event"] = "Airdrop was called in by event!",
        ["captured_by"] = "Airdrop was captured by: ",
        ["type"] = "Type",
        ["location"] = "Location",
        ["airdrop_remove"] = "Airdrop crate was automatically removed.",
        ["airdrop_remove_items"] = "Airdrop crate was removed because all items were taken.",
    }

}
```

</details>

## client/utils.lua

<details>

<summary>Click here to see a preview client.lua</summary>

```lua
-- [1. Main functions]
-- a) Function responsible for sending the notifications
function SendNotification(text, type)
    local time = 5000 -- how long should notification be on screen (in ms)
    if Config.Use_pcnotifications then
        exports['pc_notifications']:Notify(type, 'Airdrop', text, time, true)
    else
        -- Other notification system
    end
end


-- b) Joining player sync -- EXPERIMENTAL - only works when Config.UseExperimentalFeatures is set to true
AddEventHandler('playerJoinedEvent',function()
    TriggerServerEvent('pc_airdrop:server:requestAirdropSync')
end)



-- [2. Event functions]
-- a) This function is triggered when the airdrop is called
function onAirdropCall(type, location)
    if Config.LocationNotification and location then
        SendNotification((Translations.base["notification_inbound_2"]):format(type, location), 'info')
    else
        SendNotification(Translations.base["notification_inbound"], 'info')
    end
    -- *CUSTOM CODE*
    -- *EVENT TRIGGER* 
    -- ETC.
end

-- b) This function is triggered when the airdrop hits the ground (Does not trigger if Config.AirdropGroundSpawn is on)
function onAirdropHit()
    --SendNotification("Airdrop hit the ground!", 'info')
end

-- c) This function is triggered when the airdrop unlocks (Only when airdrop lock is enabled in Config)
function onAirdropUnlock()
    --SendNotification("Airdrop has been unlocked!", 'info')
end

-- d) This function is triggered when you start capturing the airdrop
function onCaptureStart() 
    SendNotification(Translations.base["notification_startcapture"], 'info')
end

-- e) This function is triggered when you successfully capture the airdrop
function onAirdropCapture() 
    SendNotification(Translations.base["notification_captured"], 'success')
end
 
-- f) This function is triggered when someone else successfully captures the airdrop
function onCaptureSomeone()
    SendNotification(Translations.base["notification_someonecaptured"], 'info')
end 

-- g) This function is triggered when you cancel capturing the airdrop
function onCaptureCancel()
    SendNotification(Translations.base["notification_capturecanceled"], 'error')
end

-- h) This function is triggered when you try to capture the airdrop but you can't
function onCaptureTryFail()
    SendNotification(Translations.base["notification_cantcapture"], 'error')
end


-- [3. Integration/technical functions]
-- a) 3D Text function, you can customize font, size etc.
function Draw3DText(x, y, z, scl_factor, text)
    local onScreen, _x, _y = World3dToScreen2d(x, y, z + 1.0)
    local p = GetGameplayCamCoords()
    local distance = GetDistanceBetweenCoords(p.x, p.y, p.z, x, y, z + 1.0, 1)
    local scale = (1 / distance) * 2
    local fov = (1 / GetGameplayCamFov()) * 100
    local scale = scale * fov * scl_factor
    if onScreen then
        SetTextScale(0.0, scale)
        SetTextFont(4)
        SetTextProportional(1)
        SetTextColour(255, 255, 255, 215)
        SetTextDropshadow(0, 0, 0, 0, 255)
        SetTextEdge(2, 0, 0, 0, 150)
        SetTextDropShadow()
        SetTextOutline()
        SetTextEntry("STRING")
        SetTextCentre(1)
        AddTextComponentString(text)
        DrawText(_x, _y)
    end
end

-- b) Help Notification function, not much to customize
function ShowHelpNotification(text)
    SetTextComponentFormat("STRING")
    AddTextComponentString(text)
    DisplayHelpTextFromStringLabel(0, 0, 1, -1)
end

-- c) Custom function that should be ran when Config.captureTextType is set to 3
function CustomCaptureFuntion(text)
    print(text) -- EXAMPLE
     -- You can add custom code here to for example: trigger a progress bar
end

-- d) Blips customization
function CreateBlips(coords, radius)
    -- Main blip
	if Config.blips.main['enabled'] then
		blips[1] = AddBlipForCoord(coords)
		SetBlipScale(blips[1], Config.blips.main['scale'])
		SetBlipSprite(blips[1], Config.blips.main['blipId'])
		SetBlipHighDetail(blips[1], true)
		SetBlipColour(blips[1], Config.blips.main['color'])
		SetBlipDisplay(blips[1], Config.blips.main['type'])
		BeginTextCommandSetBlipName("STRING")
		AddTextComponentString(Config.blips.main['name'])
		EndTextCommandSetBlipName(blips[1])
	end

    -- Radius blip
	if Config.blips.radius['enabled'] and radius then
		blips[2] = AddBlipForRadius(coords, radius + 10.0)
		SetBlipSprite(blips[2], 9)
		SetBlipColour(blips[2], Config.blips.radius['color'])
		SetBlipAlpha(blips[2], Config.blips.radius['alpha'])
	end
end

-- [4. Targeting system integration]
if Config.Use_Target then
	if Config.Target == "qtarget" then
		exports.qtarget:AddTargetModel({GetHashKey(Config.Models.crate)}, {
			options = {
				{	
					event = "pc_airdrop:client:targetcollect",
					icon = "fas fa-hand",
					label = Translations.base["TargetText"],
					canInteract = function()
						return not capturing_me and not capturing_someone and lock_timer <= 0
					end
				},
			},
			distance = 5.0,
		})
	elseif Config.Target == "ox_target" then
		exports.ox_target:addModel({GetHashKey(Config.Models.crate)}, {
			name = 'Airdrop',
			label = Translations.base["TargetText"],
			icon = "fas fa-hand",
			event = "pc_airdrop:client:targetcollect",  
			canInteract = function()
				return not capturing_me and not capturing_someone and lock_timer <= 0
			end,
			distance = 5.0,    
		})
	elseif Config.Target == "qb-target" then
        exports['qb-target']:AddTargetModel(GetHashKey(Config.Models.crate), {
            options = {
                {
                    type = "client",
                    event = "pc_airdrop:client:targetcollect",
					icon = "fas fa-hand",
                    label = Translations.base["TargetText"],
					canInteract = function()
						return not capturing_me and not capturing_someone and lock_timer <= 0
					end
                },
            },
            distance = 5.0,
        })
	end
end
```

</details>

## server/utils.lua

<details>

<summary>Click here to see a preview server.lua</summary>

```lua
-- [1. Framework + integrations]
Framework = nil

if Config.Framework == "NEW-ESX" then
    Framework = exports["es_extended"]:getSharedObject()
elseif Config.Framework == "ESX" then
    TriggerEvent('esx:getSharedObject', function(library) 
        Framework = library 
    end)
elseif Config.Framework == "QBCore" then
    Framework = exports["qb-core"]:GetCoreObject()
end
                           
function RewardItems(src, item, amount, type)
    -- If you're using esx:
    if Config.Framework == "NEW-ESX" or Config.Framework == "ESX" then
        local xPlayer = Framework.GetPlayerFromId(src)
        if xPlayer then
            if type ~= "weapon" then -- adding an item
                xPlayer.addInventoryItem(item, amount)
                -- You can add notifications here if you'd like
            else -- adding a weapon
                xPlayer.addWeapon(item, amount)
                -- You can add notifications here if you'd like
            end
        end

    -- if you're using qb-core:
    elseif Config.Framework == "QBCore" then
        local xPlayer = Framework.Functions.GetPlayer(src)
        if xPlayer then
            xPlayer.Functions.AddItem(item, amount)
            -- You can add notifications here if you'd like
        end
    end
end

-- ox_inventory integration (if you're using other grid based inventory system, you can modify this part of the code) 
-- don't touch it if you don't know what you're doing
if Config.Use_OxInventory then

    local ox_inventory = exports.ox_inventory

    ox_inventory:RegisterStash('airdrop', Translations.base["oxinv_label"], 50, 100000, false)

    function oxinvAddItem(itemName, itemCount)
        local inventory = ox_inventory:GetInventory({id = 'airdrop'})
        ox_inventory:AddItem(inventory.id, itemName, itemCount)
    end

    function oxinvClearInv()
        local inventory = ox_inventory:GetInventory({id = 'airdrop'})
        ox_inventory:ClearInventory(inventory)
    end

end


-- [2. Airdrop spawning]
-- COMMAND
-- WARNING: THIS COMMAND IS AVAILABLE FOR EVERYONE BY DEFAULT, CHANGE THE CODE BELOW TO ONLY ALLOW CERATIN PEOPLE TO CALL THE AIRDROP!
if Config.AirdropSpawnMethod['command_enabled'] then
    RegisterCommand("airdrop", function(source, args, rawCommand)
        createNewAirdrop({type = "command", src = source, arg = args})
    end)
end


-- EVENT
-- IF SET TO TRUE IN CONFIG "pc_airdrop:server:createNewAirdrop" SERVER EVENT WILL BE CREATED
-- YOU CAN CREATE YOUR OWN WAY OF CALLING AN AIRDROP USING THIS EVENT
if Config.AirdropSpawnMethod['event_enabled'] then
    RegisterNetEvent("pc_airdrop:server:createNewAirdrop", function(args)
        createNewAirdrop({type = "event", src = source, arg = args})
    end)
end
 

-- [3. Discord logs]
if Config.DiscordLogs then 

    WebhookURL = "" -- INSERT YOU DISCORD WEBHOOK HERE

    function DiscordLog(title,desc,color)
        PerformHttpRequest(WebhookURL, function(err, text, headers) end, 'POST', json.encode({
            username = "Airdrop - Logs", 
            embeds = {{
                ["color"] = color, 
                ["title"] = title,
                ["description"] = desc,
                ["footer"] = {
                    ["text"] = "PoisonCode Airdrop  •  "..os.date("%x %X %p")
                },
            }} 
        }), { 
            ['Content-Type'] = 'application/json' 
        })
    end
end
```

</details>

{% hint style="danger" %}
If you encounter any problems during configuration, you can create a support ticket on our discord.
{% endhint %}


# Tutorials

These tutorials will walk you through all the options available.

## Config.lua tutorial:

{% embed url="<https://www.youtube.com/watch?v=iC7wfVIm5KE>" %}
Config tutorial
{% endembed %}

## Other tutorials:

<details>

<summary>How to enable command only for admins (ESX)</summary>

Modify existing command code in `server/utils.lua` to look like this:

```lua
-- COMMAND
-- WARNING: THIS COMMAND IS AVAILABLE FOR EVERYONE BY DEFAULT, CHANGE THE CODE BELOW TO ONLY ALLOW CERATIN PEOPLE TO CALL THE AIRDROP!
if Config.AirdropSpawnMethod['command_enabled'] then
    RegisterCommand("airdrop", function(source, args, rawCommand)
        local xPlayer = Framework.GetPlayerFromId(source)
        if (xPlayer.group == 'superadmin' or xPlayer.group == 'admin' or xPlayer.group == 'mod') then
            createNewAirdrop({type = "command", src = source, arg = args})
        end
    end)
end
```

You can add/remove groups as required

</details>

<details>

<summary>How to enable command only for admins (QBcore/Qbox)</summary>

Soon

</details>

<details>

<summary>How to synchronize airdrops for players joining the server</summary>

Soon

</details>

<details>

<summary>How to spawn airdrop using arguments (choose place and type)</summary>

Soon

</details>


# Updates

Page dedicated to update trailers and hisotry.

### Trailers

{% embed url="<https://youtu.be/vDc8Xc22ZJE>" %}
***1.2 update trailer***
{% endembed %}

### Update history

<details>

<summary>Update 1.3.9</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.8</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.7</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.6</summary>

* Bug fix

</details>

<details>

<summary>Update 1.3.5</summary>

Introduced expermiental features:\
• client & server debug messages\
• Synchronization for joining players - [check tutorials](/scripts/pc_airdrop/tutorials#other-tutorials)

</details>

<details>

<summary>Update 1.3.4</summary>

* Target system (ox\_target/qTarget etc.) functions has been moved to client utils so you can integrate other systems or customize existing ones.

</details>

<details>

<summary>Update 1.3.3</summary>

* Airdrop spawn function will now accept arguments - [check tutorials](/scripts/pc_airdrop/tutorials#other-tutorials)

</details>

<details>

<summary>Update 1.3.2</summary>

</details>

<details>

<summary>Update 1.3.1</summary>

• Fixed a bug that blocked opening the airdrop again after the first time (when using target\&ox\_inventory) \
• Added new config option Config.RemoveWhenEmpty that removes the airdrop after all the items has been collected (when using ox\_inventory) \
• Added `["airdrop_remove_items"]` to `translations.lua`

</details>

<details>

<summary>Update 1.3</summary>

• Added blips to utils \
• Added ox\_inventory integration to utils \
• Improved both client & server side code for reliability, performance and security \
• Refactored most of the code for faster future update developement \
• Official QBCore & qb-target support \
• New (optional) notification containing airdrop name and location name \
• Added (optional) min/max item count (amount will be chosen at random between them) \
• Added option to give player weapons while not using ox\_inventory or other "weapons as items" system \
• Added automatic check for pc\_notifications, now you don't need to remove anything if you don't use it

</details>

<details>

<summary>Update 1.2</summary>

* Added a plane that flies by and drops the crate.
* Added ox\_inventory integration.
* Added qTarget/ox\_arget integration.
* Added custom functions triggered by different events (utils/client.lua).
* Added config/translations.lua file and the option to translate discord logs.
* Added the ability to change crate/parachute/plane/pilot model.
* Added the option to lock airdrop for a set period of time after it falls/spawns.
* Added the option to remove the airdrop from the map after a set period of time.
* Lots of bug fixes and optimizations.
* Restructure of folders and files to make it more clear.

</details>

<details>

<summary>Update 1.1</summary>

* Added 4 new config options regarding spawning the airdrop directly on the ground (without falling).
* Added a new collision request that should fix the error where the airdrop gets stuck in the air.
* New error handlers, if you experience any bugs there's a high chance it will show up in the F8 console.
* Minor code cleanup.

</details>


# How to update

Tutorial on how to update your resources

{% stepper %}
{% step %}

### Download the new version

Head over to <https://portal.cfx.re/assets/granted-assets> and login with fivem account you have the license to our scripts on. Head over to <mark style="color:green;">Assets</mark> then <mark style="color:green;">Granted Assest</mark> find the resource you want to update on the list and click <mark style="color:green;">download</mark>.
{% endstep %}

{% step %}

### Upload downloaded resource to your server

We recommend to make a backup of the old resource to keep the configuration, then remove the script from the server and upload the new version, alternatively, you can switch all files by hand.
{% endstep %}

{% step %}

### Reconfigure the resource

Adjust config and utils, sometimes you can reuse your old files, but if there was significant changes, you might need to spend a little more time.
{% endstep %}
{% endstepper %}


