> For the complete documentation index, see [llms.txt](https://unknown-development.gitbook.io/unknown-development/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://unknown-development.gitbook.io/unknown-development/scripts/unknown_garbagejob.md).

# Garbage Job V2

<mark style="color:$info;">Take your server's civilian jobs to the next level!</mark> <mark style="color:$info;"></mark><mark style="color:$info;">**Garbage Job V2**</mark> <mark style="color:$info;"></mark><mark style="color:$info;">is a complete overhaul of the traditional sanitation work, developed with a focus on team synergy and long-term progression.</mark>

<mark style="color:$info;">Whether you are working solo or with a crew, our system ensures an immersive, stable, and rewarding experience for every player.</mark>

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Buy yours now</td><td><a href="https://unknown-development.tebex.io/package/garbagejobv2">https://unknown-development.tebex.io/package/garbagejobv2</a></td></tr><tr><td>Checkout all our scripts</td><td><a href="https://unknown-development.tebex.io/category/resources">https://unknown-development.tebex.io/category/resources</a></td></tr></tbody></table>

## Showcase

{% embed url="<https://www.youtube.com/watch?v=qSuxmUPEUy4>" %}

## Config

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

```lua
Config = {}

-- ─── FRAMEWORK ──────────────────────────────────────────────
Config.Framework = 'qb' -- 'esx', 'qb' (Use also 'qb' for qbx)

-- ─── INTEGRATIONS ───────────────────────────────────────────
Config.Inventory   = 'ox_inventory'    -- 'ox_inventory', 'qb-inventory', 'esx'
Config.Target      = 'ox_target'       -- 'ox_target', 'qb-target'
Config.ProgressBar = 'ox_lib_circle'   -- 'ox_lib_bar', 'ox_lib_circle', 'qb-core', 'progressBars'
Config.Notify      = 'ox_lib'          -- 'ox_lib', 'qb-core', 'esx', 'okokNotify'
Config.Fuel        = 'ox_fuel'         -- 'ox_fuel', 'ps-fuel', 'cdn-fuel', 'legacyfuel'
Config.Keys        = 'qbx_vehiclekeys' -- 'qbx_vehiclekeys', 'Renewed-Vehiclekeys'
Config.TextUI      = 'ox_lib'          -- 'ox_lib', 'qb-core', 'esx', 'cd_drawtextui'
Config.Minigame    = 'bl_ui'           -- 'bl_ui', 'devhub_hackingMinigames', 'ox_lib', 'ps-ui' (only if UseMinigames = true)

-- ─── LOCALE ─────────────────────────────────────────────────
Config.Locale = 'en' -- 'en', 'lt'

-- ─── GENERAL ────────────────────────────────────────────────
Config.JobRequired   = { enable = true, job = 'garbage', minGrade = 0 }
Config.Bond          = 100    -- Bond to start the job
Config.Reward        = 75     -- Reward per bin
Config.RequireOutfit = true   -- Is uniform required to do the job
Config.Moneytype     = 'player_choose' -- 'cash', 'bank', 'money' or 'player_choose'
Config.UseMinigames  = true  -- Enable minigame on bin cleaning
Config.Debug         = false  -- Debug mode

-- ─── UI ─────────────────────────────────────────────────────
Config.UIColor = '#a78bfa' -- Accent color for the entire UI (hex format)

-- ─── LEVELS ─────────────────────────────────────────────────
Config.Levels = {
    enabled = true,
    maxLevel = 20,
    xpPerBin = 25,
    scaling = {
        xpBase = 200,               -- XP needed for level 2
        xpGrowth = 1.35,            -- XP requirement multiplier per level
        rewardPerLevel = 4,         -- +$ per level
        cooldownMin = 15,           -- Minimum cooldown at max level (minutes)
        cleaningMin = 1000,         -- Minimum cleaning duration at max level (ms)
        xpMultiplierBase = 1.0,     -- XP multiplier at level 1
        xpMultiplierPerLevel = 0.05 -- +5% XP per level
    },
    bonusItems = {
        [5]  = { name = 'sandwich', label = 'Sandwich', count = 2 },
        [10] = { name = 'water_bottle', label = 'Water Bottle', count = 3 },
        [15] = { name = 'sandwich', label = 'Sandwich', count = 5 },
        [20] = { name = 'water_bottle', label = 'Water Bottle', count = 5 },
    }
}

-- ─── MULTIPLAYER ────────────────────────────────────────────
Config.MaxGroupSize  = 4  -- Maximum players in a group
Config.InviteTimeout = 30 -- Invite timeout (seconds)

-- ─── WEBHOOKS ───────────────────────────────────────────────
Config.Webhooks = {
    ['shift_started']   = 'https://discord.com/api/webhooks/', -- Shift start logs
    ['shift_completed'] = 'https://discord.com/api/webhooks/', -- Shift end logs
    ['group_created']   = 'https://discord.com/api/webhooks/', -- Group formation logs
    ['error_logs']      = 'https://discord.com/api/webhooks/', -- Errors / Payout failures
}

-- ─── MISSIONS (DAILY/WEEKLY) ────────────────────────────────
-- Supported types: 'clean_bins', 'complete_shifts', 'earn_money'
Config.Missions = {
    daily = { -- id starts with daily_
        {
            id = 'daily_clean_50', -- MUST BE UNIQUE
            label = 'Clean 50 bins',
            type = 'clean_bins',
            goal = 50,
            reward = {
                money = 2500,
                items = {
                    { name = 'water_bottle', label = 'Water', count = 5 }
                }
            }
        },
        {
            id = 'daily_complete_3', -- MUST BE UNIQUE
            label = 'Complete 3 shifts',
            type = 'complete_shifts',
            goal = 3,
            reward = {
                money = 2000,
                items = { {name = 'water_bottle', label = 'Water', count = 2} }
            }
        }
    },
    weekly = { -- id starts with weekly_
        {
            id = 'weekly_clean_400', -- MUST BE UNIQUE
            label = 'Clean 400 bins',
            type = 'clean_bins',
            goal = 400,
            reward = {
                money = 10000,
            }
        },
        {
            id = 'weekly_earn_15000', -- MUST BE UNIQUE
            label = 'Earn $15,000 working',
            type = 'earn_money',
            goal = 15000,
            reward = {
                money = 5000,
            }
        }
    }
}

-- ─── VEHICLES ───────────────────────────────────────────────
Config.TruckModels = {
    { model = `trash2`, label = 'Garbage Truck' },
    { model = `trash`,  label = 'Garbage Truck (Old)' },
}

Config.VehicleCoords = {
    vector4(-315.9390, -1537.0000, 27.3728, 341.5793),
}

Config.ReturnLocations = {
    vector3(-316.1599, -1537.9633, 27.3823),
}

-- ─── PED ────────────────────────────────────────────────────
Config.PedName   = 's_m_y_construct_01'
Config.PedCoords = vector4(-322.1907, -1545.7968, 31.0199, 264.8837)

Config.PedAnimations = {
    default = { 'anim@amb@business@coc@coc_unpack_cut_left@', 'ped_enter_loop_boss' }
}

-- ─── CONTAINERS ─────────────────────────────────────────────
Config.PropModels         = { `prop_bin_01a`, `prop_bin_02a`, `prop_bin_03a`, `prop_bin_04a`, `prop_bin_05a` }
Config.BinGrabDuration    = 1000 -- (ms)
Config.BinDropDuration    = 500  -- (ms)
Config.BinCleaningDuration = 1800 -- (ms)
Config.BinCooldown        = 30   -- (minutes)

-- ─── ANIMATIONS ─────────────────────────────────────────────
Config.Animations = {
    grab  = { 'anim@heists@narcotics@trash', 'pickup' },
    drop  = { 'anim@heists@narcotics@trash', 'drop' },
    carry = { 'anim@heists@box_carry@', 'idle' },
}

-- ─── UNIFORM ────────────────────────────────────────────────
Config.Uniform = {
    male = {
        shirt      = { item = 65, texture = 2 },
        pants      = { item = 38, texture = 2 },
        shoes      = { item = 25,  texture = 0 },
        gloves     = { item = 0,   texture = 0 },
        undershirt = { item = 15,  texture = 0 },
        hat        = { item = -1,  texture = 0 },
        mask       = { item = 0,   texture = 0 },
        vest       = { item = 0,   texture = 0 },
        bag        = { item = 0,   texture = 0 },
        decals     = { item = 0,   texture = 0 },
        torso      = { item = 0,   texture = 0 },
    },
    female = {
        shirt      = { item = 59, texture = 2 },
        pants      = { item = 38, texture = 2 },
        shoes      = { item = 25,  texture = 0 },
        gloves     = { item = 0,   texture = 0 },
        undershirt = { item = 2,   texture = 0 },
        hat        = { item = -1,  texture = 0 },
        mask       = { item = 0,   texture = 0 },
        vest       = { item = 0,   texture = 0 },
        bag        = { item = 0,   texture = 0 },
        decals     = { item = 0,   texture = 0 },
        torso      = { item = 0,   texture = 0 },
    }
}

-- ─── BLIP ───────────────────────────────────────────────────
Config.Blip = {
    enable = true,
    coords = vector3(-322.1907, -1545.7968, 31.0199),
    sprite = 318,
    color  = 2,
    scale  = 0.9,
    label  = "Garbage Job",
}

-- ─── TUTORIAL ───────────────────────────────────────────────
Config.Tutorial = {
    enabled = true,
    pages = {
        {
            title = 'Picking Up Bins',
            text  = 'Drive your garbage truck around the city and look for trash bins on the sidewalks. Walk up to a bin and use the target to pick it up. Your character will grab the bin and carry it — you cannot enter vehicles while holding a bin. Supported bin types include standard city bins.',
            image = 'img/tutorial1.jpg',
        },
        {
            title = 'Cleaning & Emptying',
            text  = 'Carry the bin to the back of your garbage truck. When you are close enough, an target option will appear to empty the bin. Use the target to complete the cleaning process. Once done, place bin on the ground and you will receive a payment for each bin cleaned. Bins have a cooldown period before they can be cleaned again.',
            image = 'img/tutorial2.jpg',
        },
        {
            title = 'Working as a Team',
            text  = 'You can invite other players to work with you! Before starting the job, enter a player\'s server ID in the team section and send an invite. They will receive a popup to accept or decline. Once your team is assembled, you can adjust the pay split percentages for each member. All members share the same truck and bin counter.',
            image = 'img/tutorial3.jpg',
        },
        {
            title = 'Finishing the Job',
            text  = 'When you are done collecting trash, drive the garbage truck back to the return point. Park the truck in the designated area and confirm the return. Your bond will be refunded and you will receive your total earnings. If you are in a group, each member gets paid according to the agreed percentage split. Good luck!',
            image = 'img/tutorial4.jpg',
        },
    }
}
```

{% endtab %}

{% tab title="shared/framework.lua" %}

```lua
local Framework = {}

function Framework:GetCurrent()
    if Config.Framework == 'esx' and GetResourceState('es_extended') == 'started' then
        return 'esx'
    elseif Config.Framework == 'qb' and GetResourceState('qb-core') == 'started' then
        return 'qb'
    end
    return nil
end


function Framework:AddMoney(source, account, amount, reason)
    local framework = self:GetCurrent()
    local acc = account
    if not acc or acc == 'player_choose' then acc = 'cash' end
    
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = QBCore.Functions.GetPlayer(source)
        if Player then
            Player.Functions.AddMoney(acc, amount, reason)
            return true
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer then
            if acc == 'cash' or acc == 'money' then
                xPlayer.addMoney(amount)
            else
                xPlayer.addAccountMoney('bank', amount)
            end
            return true
        end
    end
    return false
end

function Framework:RemoveMoney(source, account, amount, reason)
    local framework = self:GetCurrent()
    local acc = account
    if not acc or acc == 'player_choose' then acc = 'bank' end
    
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = QBCore.Functions.GetPlayer(source)
        if Player then
            return Player.Functions.RemoveMoney(acc, amount, reason)
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer then
            if acc == 'cash' or acc == 'money' then
                return xPlayer.removeMoney(amount)
            else
                return xPlayer.removeAccountMoney('bank', amount)
            end
        end
    end
    return false
end

function Framework:GetMoney(source, account)
    local framework = self:GetCurrent()

    if Config.Inventory == 'ox_inventory' then
        if account == 'cash' or account == 'money' then
            return exports.ox_inventory:GetItemCount(source, 'money')
        elseif account == 'black' or account == 'black_money' then
            return exports.ox_inventory:GetItemCount(source, 'black_money')
        end
    end
    
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = QBCore.Functions.GetPlayer(source)
        if Player then
            return Player.Functions.GetMoney(account)
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer then
            if account == 'cash' or account == 'money' then
                return xPlayer.getMoney()
            elseif account == 'bank' then
                return xPlayer.getAccount('bank').money
            end
        end
    end
    return 0
end

function Framework:GetName(source)
    local framework = self:GetCurrent()
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = QBCore.Functions.GetPlayer(source)
        if Player then
            return Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer then
            return xPlayer.getName()
        end
    end
    return GetPlayerName(source) or ("Player " .. source)
end

function Framework:GiveItem(source, itemName, count)
    local framework = self:GetCurrent()
    if Config.Inventory == 'ox_inventory' then
        exports.ox_inventory:AddItem(source, itemName, count)
        return true
    end

    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = QBCore.Functions.GetPlayer(source)
        if Player then
            return Player.Functions.AddItem(itemName, count)
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer then
            xPlayer.addInventoryItem(itemName, count)
            return true
        end
    end
    return false
end

function Framework:HasJob(source, jobName, minGrade)
    if not jobName then return true end
    minGrade = minGrade or 0
    
    local framework = self:GetCurrent()
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = IsDuplicityVersion() and QBCore.Functions.GetPlayer(source) or QBCore.Functions.GetPlayerData()
        if Player and Player.job then
            return Player.job.name == jobName and Player.job.grade.level >= minGrade
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = IsDuplicityVersion() and ESX.GetPlayerFromId(source) or ESX.GetPlayerData()
        if xPlayer and xPlayer.job then
            return xPlayer.job.name == jobName and xPlayer.job.grade >= minGrade
        end
    end
    return false
end

function Framework:GetIdentifier(source)
    local framework = self:GetCurrent()
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local Player = IsDuplicityVersion() and QBCore.Functions.GetPlayer(source) or QBCore.Functions.GetPlayerData()
        if Player then
            return Player.PlayerData.citizenid
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = IsDuplicityVersion() and ESX.GetPlayerFromId(source) or ESX.GetPlayerData()
        if xPlayer then
            return xPlayer.identifier
        end
    end
    if IsDuplicityVersion() then
        for i = 0, GetNumPlayerIdentifiers(source) - 1 do
            local id = GetPlayerIdentifier(source, i)
            if string.match(id, "license:") then
                return id
            end
        end
    end
    return nil
end

function Framework:IsAdmin(source)
    local framework = self:GetCurrent()
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        return QBCore.Functions.HasPermission(source, 'admin') or QBCore.Functions.HasPermission(source, 'god')
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local xPlayer = ESX.GetPlayerFromId(source)
        if xPlayer then
            local group = xPlayer.getGroup()
            return group == 'admin' or group == 'superadmin'
        end
    end
    return false
end

return Framework
```

{% endtab %}

{% tab title="shared/minigames.lua" %}

```lua
function StartMinigame(callback)
    if Config.Minigame == 'ox_lib' and GetResourceState('ox_lib') == 'started' then
        local success = lib.skillCheck({'easy', 'easy', 'easy'}, {'e', 'e', 'e', 'e'})
        if callback then callback(success) end
        return success
        
    elseif Config.Minigame == 'bl_ui' and GetResourceState('bl_ui') == 'started' then
        local success = exports['bl_ui']:RapidLines(2, 70, 3)
        if callback then callback(success) end
        return success
        
    elseif Config.Minigame == 'devhub_hackingMinigames' and GetResourceState('devhub_hackingMinigames') == 'started' then
        local success = exports['devhub_hackingMinigames']:startMinigame('morse', 60)
        if callback then callback(success) end
        return success

    elseif Config.Minigame == 'devhub_minigames' and GetResourceState('devhub_minigames') == 'started' then
        local success = exports['devhub_minigames']:startMinigame("minigame_2", "medium")
        if callback then callback(success) end
        return success
    
    elseif Config.Minigame == 'ps-ui' and GetResourceState('ps-ui') == 'started' then
        local success = exports['ps-ui']:Thermite(10, 5, 3) -- Time, Gridsize, IncorrectBlocks
        if callback then callback(success) end
        return success
        
    elseif Config.Minigame == 'unknown_skillBar' and GetResourceState('unknown_skillBar') == 'started' then
        local success = exports['unknown_skillBar']:skillBar(70, 3) -- Difficulty, Iterations
        if callback then callback(success) end
        return success
    end

end
```

{% endtab %}

{% tab title="shared/notify.lua" %}

```lua
function Notify(source, message, type)
    if not source or source == 0 then
        -- Client-side
        if Config.Notify == 'ox_lib' and GetResourceState('ox_lib') == 'started' then
            lib.notify({
                title = 'Garbage Job',
                description = message,
                type = type or 'inform'
            })
        elseif Config.Notify == 'qb-core' and GetResourceState('qb-core') == 'started' then
            local QBCore = exports['qb-core']:GetCoreObject()
            if type == 'error' then
                QBCore.Functions.Notify(message, 'error')
            elseif type == 'success' then
                QBCore.Functions.Notify(message, 'success')
            else
                QBCore.Functions.Notify(message, 'primary')
            end
        elseif Config.Notify == 'esx' and GetResourceState('es_extended') == 'started' then
            local ESX = exports['es_extended']:getSharedObject()
            ESX.ShowNotification(message)
        elseif Config.Notify == 'okokNotify' and GetResourceState('okokNotify') == 'started' then
            exports['okokNotify']:Alert('Garbage Job', message, 5000, type or 'info', true)
        else
            -- Fallback GTA notification
            BeginTextCommandThefeedPost('STRING')
            AddTextComponentSubstringPlayerName(message)
            EndTextCommandThefeedPostTicker(true, true)
        end
    else
        -- Server-side
        if Config.Notify == 'ox_lib' then
            TriggerClientEvent('ox_lib:notify', source, {
                description = message,
                type = type or 'inform'
            })
        elseif Config.Notify == 'qb-core' then
            TriggerClientEvent('QBCore:Notify', source, message, type or 'primary')
        elseif Config.Notify == 'esx' then
            TriggerClientEvent('esx:showNotification', source, message)
        elseif Config.Notify == 'okokNotify' then
            TriggerClientEvent('okokNotify:Alert', source, 'Garbage Job', message, 5000, type or 'info', true)
        end
    end
end
```

{% endtab %}

{% tab title="shared/progressbar.lua" %}

```lua
function ProgressBar(source, label, duration, options)
    options = options or {}
        -- Client-side
        if Config.ProgressBar == 'ox_lib_bar' and GetResourceState('ox_lib') == 'started' then
            if lib.progressBar({
                duration = duration,
                label = label,
                useWhileDead = options.useWhileDead or false,
                canCancel = options.canCancel or true,
                disable = options.disable or {
                    car = true,
                    move = true,
                    combat = true
                },
                anim = options.anim or {},
                onCancel = options.onCancel,
                onFinish = options.onFinish
            }) then
                return true
            else
                if options.onCancel then options.onCancel() end
                return false
            end
        elseif Config.ProgressBar == 'ox_lib_circle' and GetResourceState('ox_lib') == 'started' then
            if lib.progressCircle({
                duration = duration,
                position = 'bottom',
                label = label,
                useWhileDead = options.useWhileDead or false,
                canCancel = options.canCancel or true,
                disable = options.disable or {
                    car = true,
                    move = true,
                    combat = true
                },
                anim = options.anim or {},
                onCancel = options.onCancel,
                onFinish = options.onFinish
            }) then
                return true
            else
                if options.onCancel then options.onCancel() end
                return false
            end
            
        elseif Config.ProgressBar == 'qb-core' and GetResourceState('qb-core') == 'started' then
            local QBCore = exports['qb-core']:GetCoreObject()
            QBCore.Functions.Progressbar('cleaning_bin_progress', label, duration, false, true, {
                disableMovement = true,
                disableCarMovement = true,
                disableMouse = false,
                disableCombat = true,
            }, options.anim or {}, {}, {}, function()
                if options.onFinish then options.onFinish() end
            end, function()
                if options.onCancel then options.onCancel() end
            end)
            return true
            
        elseif Config.ProgressBar == 'progressBars' and GetResourceState('progressBars') == 'started' then
            exports['progressBars']:startUI(duration, label)
            Wait(duration)
            return true
            
        else
            -- Fallback progress bar
            local startTime = GetGameTimer()
            while GetGameTimer() - startTime < duration do
                Wait(0)
                local elapsed = GetGameTimer() - startTime
                local progress = elapsed / duration
                
                SetTextFont(4)
                SetTextScale(0.5, 0.5)
                SetTextColour(255, 255, 255, 255)
                SetTextOutline()
                BeginTextCommandDisplayText("STRING")
                AddTextComponentSubstringPlayerName(label)
                EndTextCommandDisplayText(0.5, 0.9)
                
                DrawRect(0.5, 0.95, 0.2, 0.03, 0, 0, 0, 150)
                DrawRect(0.5, 0.95, 0.2 * progress, 0.025, 255, 255, 255, 200)
                
                if IsControlJustPressed(0, 200) then -- ESC pressed
                    return false
                end
            end
        return true
    end
end
```

{% endtab %}

{% tab title="shared/fuel.lua" %}

```lua
function SetFuel(vehicle, fuelLevel)
    if Config.Fuel == 'ox_fuel' and GetResourceState('ox_fuel') == 'started' then
        Entity(vehicle).state.fuel = fuelLevel
    elseif Config.Fuel == 'ps-fuel' and GetResourceState('ps-fuel') == 'started' then
        exports['ps-fuel']:SetFuel(vehicle, fuelLevel)
    elseif Config.Fuel == 'cdn-fuel' and GetResourceState('cdn-fuel') == 'started' then
        exports['cdn-fuel']:SetFuel(vehicle, fuelLevel)
    elseif Config.Fuel == 'legacyfuel' and GetResourceState('legacyfuel') == 'started' then
        exports['LegacyFuel']:SetFuel(vehicle, fuelLevel)
    end
end

function GetFuel(vehicle)
    if Config.Fuel == 'ox_fuel' and GetResourceState('ox_fuel') == 'started' then
        return Entity(vehicle).state.fuel or 100.0
    elseif Config.Fuel == 'ps-fuel' and GetResourceState('ps-fuel') == 'started' then
        return exports['ps-fuel']:GetFuel(vehicle)
    elseif Config.Fuel == 'cdn-fuel' and GetResourceState('cdn-fuel') == 'started' then
        return exports['cdn-fuel']:GetFuel(vehicle)
    elseif Config.Fuel == 'legacyfuel' and GetResourceState('legacyfuel') == 'started' then
        return exports['LegacyFuel']:GetFuel(vehicle)
    end
    return 100.0
end
```

{% endtab %}

{% tab title="shared/keys.lua" %}

```lua
function GiveKeys(source, vehicle, plate)
    if Config.Keys == 'qbx_vehiclekeys' and GetResourceState('qbx_vehiclekeys') == 'started' then
        exports['qbx_vehiclekeys']:GiveKeys(source, vehicle, plate or false)
    elseif Config.Keys == 'Renewed-Vehiclekeys' and GetResourceState('Renewed-Vehiclekeys') == 'started' then
        exports['Renewed-Vehiclekeys']:addKey(plate)
    end
end

function RemoveKeys(source, vehicle, plate)
    if Config.Keys == 'qbx_vehiclekeys' and GetResourceState('qbx_vehiclekeys') == 'started' then
        exports['qbx_vehiclekeys']:RemoveKeys(source, vehicle)
    elseif Config.Keys == 'Renewed-Vehiclekeys' and GetResourceState('Renewed-Vehiclekeys') == 'started' then
        exports['Renewed-Vehiclekeys']:removeKey(plate)
    end
end
```

{% endtab %}

{% tab title="shared/target.lua" %}

```lua
function AddEntity(entity, options)
    if Config.Target == 'ox_target' and GetResourceState('ox_target') == 'started' then
        exports.ox_target:addLocalEntity(entity, options)
    elseif Config.Target == 'qb-target' and GetResourceState('qb-target') == 'started' then
        exports['qb-target']:AddTargetEntity(entity, {
            options = {
                {
                    type = "client",
                    event = options.onSelect,
                    icon = options.icon,
                    label = options.label,
                }
            },
            distance = options.distance or 2.0
        })
    end
end

function RemoveEntity(entity)
    if Config.Target == 'ox_target' and GetResourceState('ox_target') == 'started' then
        exports.ox_target:removeLocalEntity(entity)
    elseif Config.Target == 'qb-target' and GetResourceState('qb-target') == 'started' then
        exports['qb-target']:RemoveTargetEntity(entity)
    end
end

function AddModel(model, options)
    if Config.Target == 'ox_target' and GetResourceState('ox_target') == 'started' then
        exports.ox_target:addModel(model, options)
    elseif Config.Target == 'qb-target' and GetResourceState('qb-target') == 'started' then
        exports['qb-target']:AddTargetModel(model, {
            options = {
                {
                    type = "client",
                    event = options.onSelect,
                    icon = options.icon,
                    label = options.label,
                }
            },
            distance = options.distance or 2.0
        })
end
```

{% endtab %}

{% tab title="shared/textui.lua" %}

```lua
local TextUIConfig = {
    ox_lib = {
        position = 'top-center',
        icon = 'fa-solid fa-truck',
        iconColor = '#3FB1FF'
    },
    
    qbcore = {
        position = 'bottom',
        icon = 'fas fa-truck',
        style = {}
    },
    
    esx = {
        position = 'bottom',
        style = {}
    },
    
    cd_drawtextui = {
        position = 'right'
    },
}

function TextUIShow(text, options)
    options = options or {}
    local config = TextUIConfig[Config.TextUI or 'none'] or {}
    
    if Config.TextUI == 'ox_lib' and GetResourceState('ox_lib') == 'started' then
        lib.showTextUI(text, {
            position = options.position or config.position or 'top-center',
            icon = options.icon or config.icon or 'fa-solid fa-circle-info',
            iconColor = options.iconColor or config.iconColor or '#3FB1FF',
            style = options.style or {}
        })
        
    elseif Config.TextUI == 'qb-core' and GetResourceState('qb-core') == 'started' then
        exports['qb-core']:DrawText(text, options.position or config.position or 'bottom')
        
    elseif Config.TextUI == 'esx' and GetResourceState('es_extended') == 'started' then
        exports['esx_textui']:Show(text, options.position or config.position or 'bottom')
        
    elseif Config.TextUI == 'cd_drawtextui' and GetResourceState('cd_drawtextui') == 'started' then
        exports['cd_drawtextui']:Show(text, options.position or config.position or 'right')

    else
        print("No TextUI system available to show.")
    end
    
    return true
end


function TextUIHide()

    if Config.TextUI == 'ox_lib' and GetResourceState('ox_lib') == 'started' then
        lib.hideTextUI()
        
    elseif Config.TextUI == 'qb-core' and GetResourceState('qb-core') == 'started' then
        exports['qb-core']:HideText()
        
    elseif Config.TextUI == 'esx' and GetResourceState('es_extended') == 'started' then
        if GetResourceState('esx_textui') == 'started' then
            exports['esx_textui']:Hide()
        end
        
    elseif Config.TextUI == 'cd_drawtextui' and GetResourceState('cd_drawtextui') == 'started' then
        exports['cd_drawtextui']:Hide()

    else
        print("No TextUI system available to hide.")
    end
    
    return true
end
```

{% endtab %}

{% tab title="shared/webhooks.lua" %}

```lua
function SendWebhook(webhookType, data)
    if not IsDuplicityVersion() then return end
    
    local webhookURL = Config.Webhooks[webhookType]
    if not webhookURL or webhookURL == '' then return end

    local playerId = data.source
    local playerName = GetPlayerName(playerId) or "Unknown"
    local identifiers = GetPlayerIdentifiers(playerId)
    local playerSteam, playerLicense, playerDiscord, playerIP = 'N/A', 'N/A', 'N/A', 'N/A'

    for _, id in ipairs(identifiers) do
        if string.find(id, "steam:") then playerSteam = id
        elseif string.find(id, "license:") then playerLicense = id
        elseif string.find(id, "discord:") then playerDiscord = id
        elseif string.find(id, "ip:") then playerIP = id end
    end

    local embed = {
        {
            ["title"] = data.title or "Garbage Job",
            ["color"] = data.color or 16711680,
            ["fields"] = {
                {
                    ["name"] = "Player Info",
                    ["value"] = string.format("**Name:** `%s`\n**ID:** `%s`\n**Steam:** `%s`\n**License:** `%s`\n**Discord:** `%s`\n**IP:** ||%s||", 
                        playerName, playerId, playerSteam, playerLicense, playerDiscord, playerIP),
                    ["inline"] = false
                }
            },
            ["timestamp"] = os.date("!%Y-%m-%dT%H:%M:%SZ"),
            ["footer"] = { ["text"] = "𝔲𝔫𝔨𝔫𝔬𝔴𝔫 Development" }
        }
    }

    if webhookType == 'shift_started' then
        embed[1].description = "🚛 **Shift Started**"
        embed[1].color = 3447003
        table.insert(embed[1].fields, {
            ["name"] = "Details",
            ["value"] = string.format("**Type:** `%s`\n**Truck:** `%s`", data.jobType, data.truck),
            ["inline"] = true
        })
    elseif webhookType == 'shift_completed' then
        embed[1].description = "✅ **Shift Completed**"
        embed[1].color = 65280
        table.insert(embed[1].fields, {
            ["name"] = "Earnings",
            ["value"] = string.format("**Money:** `$%s`\n**Bins:** `%s`", data.pay, data.bins),
            ["inline"] = true
        })
    elseif webhookType == 'group_created' then
        embed[1].description = "👥 **Group Created**"
        embed[1].color = 16776960
    elseif webhookType == 'error_logs' then
        embed[1].description = "⚠️ **System Error**"
        embed[1].color = 16711680
        table.insert(embed[1].fields, {
            ["name"] = "Error Message",
            ["value"] = data.message or "Unknown error",
            ["inline"] = false
        })
    end

    PerformHttpRequest(webhookURL, function(err, text, headers) end, 'POST', json.encode({
        username = "Garbage Job",
        avatar_url = "https://i.ibb.co/SwWYnG0r/unknown-development.png",
        embeds = embed
    }), {['Content-Type'] = 'application/json'})
end

```

{% endtab %}
{% endtabs %}

## Locales

{% tabs %}
{% tab title="locales/locales.lua" %}

```lua
Locales = {
    ['en'] = {
        -- UI
        ['panel_title_1'] = 'GARBAGE',
        ['panel_title_2'] = 'JOB',
        ['panel_info'] = 'Select a vehicle and start working. You can work alone or invite other players.',
        ['tab_vehicles'] = 'Vehicles',
        ['tab_missions'] = 'Missions',
        ['tab_level'] = 'Level',
        ['sl_transport'] = 'VEHICLE',
        ['sl_info'] = 'INFORMATION',
        ['sl_team'] = 'TEAM',
        ['payment_method'] = 'Payment Method',
        ['vs_title'] = 'Select Vehicle',
        ['bond_label'] = 'Bond',
        ['start_solo'] = 'Start Solo',
        ['create_group'] = 'Create Group',
        ['gp_title'] = 'Group Management',
        ['gp_invite'] = 'Invite',
        ['gp_members'] = 'Members',
        ['gp_equal'] = 'Split Equally',
        ['gp_start'] = 'Start Job',
        ['gp_total'] = 'Total',
        ['gp_back'] = 'Back',
        ['invite_placeholder'] = 'Player ID',
        ['sl_daily_missions'] = 'Daily Missions',
        ['sl_weekly_missions'] = 'Weekly Missions',
        ['no_missions'] = 'No active missions',
        ['mission_claim'] = 'Claim Reward',
        ['mission_claimed'] = 'Claimed',
        ['level_title'] = 'Progression',
        ['level_label'] = 'Current Level',
        ['xp_progress'] = 'XP Progress',
        ['perk_reward'] = 'Bonus Reward',
        ['perk_cooldown'] = 'Bin Reset Time',
        ['perk_cleaning'] = 'Cleaning Time',
        ['perk_xp_mult'] = 'XP Multiplier',
        ['perk_max_level'] = 'Max Level Reached',
        ['level_bonus_items'] = 'Upcoming Rewards',
        ['garbage_job'] = 'Garbage Job',
        ['pickup_trashcan'] = 'Pickup Trashcan',
        ['clean_bin_label'] = 'Clean Bin',
        ['drop_bin_hint'] = 'X - Drop bin',
        ['return_truck_hint'] = 'E - Return Truck',
        ['textui_truck'] = 'Truck',
        ['textui_trash'] = 'Trash',
        ['tracker_task'] = 'Collect and clean trash bins',
        ['tracker_cleaned'] = 'Cleaned',
        ['picking_up_bin'] = 'Picking up bin..',
        ['dropping_bin'] = 'Dropping bin..',
        ['cleaning_bin'] = 'Cleaning Bin..',
        ['start_duty'] = 'Start Duty',
        ['end_duty'] = 'End Duty',
        ['cancel'] = 'Cancel',
        ['invite_title'] = 'Job Invite',
        ['invite_desc'] = '%s invites you to work',
        ['accept'] = 'Accept',
        ['reject'] = 'Reject',
        ['confirm_title'] = 'Cancel job?',
        ['confirm_desc'] = 'All group members will be removed from the lobby.',
        ['confirm_yes'] = 'Yes, cancel',
        ['confirm_no'] = 'No, go back',
        ['rejoin_title'] = 'Continue job?',
        ['rejoin_desc'] = 'You have an active group garbage job in progress. Re-join it?',
        ['rejoin_yes'] = 'Yes, rejoin',
        ['rejoin_no'] = 'No, leave group',
        ['tutorial_ok'] = 'OK',
        ['tutorial_dismiss'] = "Don't show again",
        ['tutorial_next'] = 'Next',
        ['tutorial_prev'] = 'Previous',

        -- Notify
        ['now_on_duty'] = 'You are now on duty',
        ['now_off_duty'] = 'You are now off duty',
        ['bin_cleaned'] = 'Bin Cleaned! +$%s',
        ['truck_returned'] = 'Truck Returned',
        ['truck_spawned'] = 'Garbage truck spawned',
        ['outfit_restored'] = 'Outfit restored',
        ['uniform_equipped'] = 'Uniform equipped',
        ['payout_received'] = 'You received $%s for garbage job',
        ['mission_reward_claimed'] = 'Reward claimed!',
        ['level_up'] = 'Level Up! You reached level %s',
        ['level_bonus_item'] = 'You received a bonus item for leveling up!',
        ['already_in_vehicle'] = 'You are in a vehicle!',
        ['already_holding_bin'] = 'You are already holding a bin!',
        ['not_on_duty'] = 'You are not on duty!',
        ['bin_empty_wait'] = 'This trashcan is empty! Wait %s minutes',
        ['too_far'] = 'You are too far away!',
        ['you_dont_have_bin'] = "You don't have a bin.",
        ['bin_already_emptied'] = "This bin has already been emptied.",
        ['minigame_failed'] = 'Failed the minigame!',
        ['must_be_on_duty'] = 'You must be on duty to spawn a vehicle!',
        ['cannot_spawn_in_vehicle'] = 'You cannot spawn a vehicle while in a vehicle!',
        ['vehicle_already_spawned'] = 'Vehicle already spawned!',
        ['put_on_uniform'] = 'Put on your uniform first!',
        ['no_outfits_saved'] = 'No outfit saved to restore!',
        ['not_enough_money'] = 'Not enough money!',
        ['spawn_area_busy'] = 'The Spawn Area is busy',
        ['spawn_failed'] = 'Failed to spawn truck! Try again.',
        ['wrong_job'] = 'You do not have the right job or rank for this!',
        ['invite_sent'] = 'Invite sent',
        ['invite_rejected'] = 'Player rejected the invite',
        ['invite_expired'] = 'Invite expired',
        ['group_full'] = 'Group is full!',
        ['player_not_found'] = 'Player not found!',
        ['player_already_in_group'] = 'Player is already in a group!',
        ['player_joined'] = 'Player joined the group',
        ['player_left'] = 'Player left the group',
        ['group_dissolved'] = 'Group has been dissolved',
        ['duty_ended_group'] = 'The job has ended',
        ['split_not_100'] = 'Pay split must equal 100%!',
        ['group_cancelled'] = 'The group leader cancelled the job!',
        ['player_returned'] = 'Player %s has reconnected and rejoined the group!',
        ['admin_xp_added'] = 'XP added!',
        ['admin_xp_removed'] = 'XP removed!',
        ['admin_level_set'] = 'Level set!',
        ['admin_player_not_found'] = 'Player not found!',
        ['admin_addxp_help'] = 'Add XP to a player (Admin Only)',
        ['admin_removexp_help'] = 'Remove XP from a player (Admin Only)',
        ['admin_setlevel_help'] = 'Set a player level (Admin Only)',
        ['admin_id_param'] = 'Player Server ID',
        ['admin_amount_param'] = 'XP Amount',
        ['admin_level_param'] = 'New Level',
    },

    ['lt'] = {
        -- UI
        ['panel_title_1'] = 'ŠIUKŠLIŲ',
        ['panel_title_2'] = 'DARBAS',
        ['panel_info'] = 'Pasirinkite transportą ir pradėkite darbą. Galite dirbti vieni arba pakviesti kitus žaidėjus.',
        ['tab_vehicles'] = 'Transportas',
        ['tab_missions'] = 'Misijos',
        ['tab_level'] = 'Lygis',
        ['sl_transport'] = 'TRANSPORTAS',
        ['sl_info'] = 'INFORMACIJA',
        ['sl_team'] = 'KOMANDA',
        ['payment_method'] = 'Apmokėjimas',
        ['vs_title'] = 'Pasirinkite transportą',
        ['bond_label'] = 'Užstatas',
        ['start_solo'] = 'Pradėti Solo',
        ['create_group'] = 'Sukurti Grupę',
        ['gp_title'] = 'Grupės valdymas',
        ['gp_invite'] = 'Pakviesti',
        ['gp_members'] = 'Nariai',
        ['gp_equal'] = 'Padalinti lygiai',
        ['gp_start'] = 'Pradėti darbą',
        ['gp_total'] = 'Viso',
        ['gp_back'] = 'Atgal',
        ['invite_placeholder'] = 'Žaidėjo ID',
        ['sl_daily_missions'] = 'Dienos Misijos',
        ['sl_weekly_missions'] = 'Savaitės Misijos',
        ['no_missions'] = 'Nėra aktyvių misijų',
        ['mission_claim'] = 'Atsiimti prizą',
        ['mission_claimed'] = 'Atsiimta',
        ['level_title'] = 'Progresas',
        ['level_label'] = 'Dabartinis lygis',
        ['xp_progress'] = 'XP Progresas',
        ['perk_reward'] = 'Papildomas uždarbis',
        ['perk_cooldown'] = 'Šiukšlinių atsistatymas',
        ['perk_cleaning'] = 'Valymo trukmė',
        ['perk_xp_mult'] = 'XP Multiplikatorius',
        ['perk_max_level'] = 'Pasiektas maksimalus lygis',
        ['level_bonus_items'] = 'Artimiausi apdovanojimai',
        ['garbage_job'] = 'Šiukšlių darbas',
        ['pickup_trashcan'] = 'Paimti šiukšlinę',
        ['clean_bin_label'] = 'Išvalyti konteinerį',
        ['drop_bin_hint'] = 'X - Padėti konteinerį',
        ['return_truck_hint'] = 'E - Grąžinti sunkvežimį',
        ['textui_truck'] = 'Sunkvežimis',
        ['textui_trash'] = 'Šiukšlinė',
        ['tracker_task'] = 'Surinkite ir išvalykite šiukšles',
        ['tracker_cleaned'] = 'Išvalyta',
        ['picking_up_bin'] = 'Imamas konteineris..',
        ['dropping_bin'] = 'Padedamas konteineris..',
        ['cleaning_bin'] = 'Valomas konteineris..',
        ['start_duty'] = 'Pradėti darbą',
        ['end_duty'] = 'Baigti darbą',
        ['cancel'] = 'Atšaukti',
        ['invite_title'] = 'Darbo kvietimas',
        ['invite_desc'] = '%s kviečia jus dirbti',
        ['accept'] = 'Priimti',
        ['reject'] = 'Atmesti',
        ['confirm_title'] = 'Atšaukti darbą?',
        ['confirm_desc'] = 'Visi grupės nariai bus pašalinti iš laukimo.',
        ['confirm_yes'] = 'Taip, atšaukti',
        ['confirm_no'] = 'Ne, grįžti',
        ['rejoin_title'] = 'Tęsti darbą?',
        ['rejoin_desc'] = 'Turite aktyvų šiukšliavežių darbą. Ar norite prisijungti atgal?',
        ['rejoin_yes'] = 'Taip, grįžti',
        ['rejoin_no'] = 'Ne, palikti grupę',
        ['tutorial_ok'] = 'Gerai',
        ['tutorial_dismiss'] = 'Daugiau neberodyti',
        ['tutorial_next'] = 'Sekantis',
        ['tutorial_prev'] = 'Ankstesnis',

        -- Notify
        ['now_on_duty'] = 'Jūs pradėjote darbą',
        ['now_off_duty'] = 'Jūs baigėte darbą',
        ['bin_cleaned'] = 'Konteineris išvalytas! +$%s',
        ['truck_returned'] = 'Sunkvežimis grąžintas',
        ['truck_spawned'] = 'Iškvietėte šiukšliavežį',
        ['outfit_restored'] = 'Apranga atstatyta',
        ['uniform_equipped'] = 'Uniforma užsidėta',
        ['payout_received'] = 'Gavote $%s už šiukšlių darbą',
        ['mission_reward_claimed'] = 'Prizas atsiimtas!',
        ['level_up'] = 'Pakilo lygis! Pasiekėte %s lygį',
        ['level_bonus_item'] = 'Gavote papildomą daiktą už pakilusį lygį!',
        ['already_in_vehicle'] = 'Jūs esate transporto priemonėje!',
        ['already_holding_bin'] = 'Jūs jau laikote šiukšlinę!',
        ['not_on_duty'] = 'Jūs nesate pamainoje!',
        ['bin_empty_wait'] = 'Ši šiukšlinė tuščia! Palaukite %s minutes',
        ['too_far'] = 'Esate per toli!',
        ['you_dont_have_bin'] = 'Neturite konteinerio.',
        ['bin_already_emptied'] = 'Šis konteineris jau ištuštintas.',
        ['minigame_failed'] = 'Minigame nepavyko!',
        ['must_be_on_duty'] = 'Turite būti pamainoje, kad iškviestumėte transportą!',
        ['cannot_spawn_in_vehicle'] = 'Negalite iškviesti transporto būdami transporto priemonėje!',
        ['vehicle_already_spawned'] = 'Transportas jau iškviestas!',
        ['put_on_uniform'] = 'Užsidėkite uniformą pirmiausia!',
        ['no_outfits_saved'] = 'Nėra išsaugotos aprangos, kurią būtų galima atstatyti!',
        ['not_enough_money'] = 'Neužtenka pinigų!',
        ['spawn_area_busy'] = 'Išvykimo vieta užimta',
        ['spawn_failed'] = 'Nepavyko iškviesti sunkvežimio! Bandykite dar kartą.',
        ['wrong_job'] = 'Neturite reikiamo darbo arba rango šiam darbui!',
        ['invite_sent'] = 'Kvietimas išsiųstas',
        ['invite_rejected'] = 'Žaidėjas atmetė kvietimą',
        ['invite_expired'] = 'Kvietimas pasibaigė',
        ['group_full'] = 'Grupė pilna!',
        ['player_not_found'] = 'Žaidėjas nerastas!',
        ['player_already_in_group'] = 'Žaidėjas jau grupėje!',
        ['player_joined'] = 'Žaidėjas prisijungė prie grupės',
        ['player_left'] = 'Žaidėjas paliko grupę',
        ['group_dissolved'] = 'Grupė išardyta',
        ['duty_ended_group'] = 'Darbas baigtas',
        ['split_not_100'] = 'Procentai turi būti lygūs 100%!',
        ['group_cancelled'] = 'Vadovas atšaukė darbą!',
        ['player_returned'] = 'Žaidėjas %s prisijungė ir grįžo į grupę!',
        ['admin_xp_added'] = 'XP pridėta!',
        ['admin_xp_removed'] = 'XP pašalinta!',
        ['admin_level_set'] = 'Lygis nustatytas!',
        ['admin_player_not_found'] = 'Žaidėjas nerastas!',
        ['admin_addxp_help'] = 'Pridėti XP žaidėjui (Tik Adminams)',
        ['admin_removexp_help'] = 'Pašalinti XP žaidėjui (Tik Adminams)',
        ['admin_setlevel_help'] = 'Nustatyti žaidėjo lygį (Tik Adminams)',
        ['admin_id_param'] = 'Žaidėjo ID',
        ['admin_amount_param'] = 'XP kiekis',
        ['admin_level_param'] = 'Naujas lygis',
    }
}

```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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://unknown-development.gitbook.io/unknown-development/scripts/unknown_garbagejob.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.
