# Open files preview

{% 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.poisoncode.com/group-1/pc_airdrop/pc_airdrop-legacy/open-files-preview.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
