> 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_carloot.md).

# Car Loot

*<mark style="color:$info;">Are you searching for a realistic and rewarding criminal activity for your FiveM server? Look no further! Our Car Loot (Radio Theft) system delivers a modern and immersive stealing experience designed for long-term roleplay.</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/carloot">https://unknown-development.tebex.io/package/carloot</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=xefq1282Hb4>" %}

## Config

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

```lua
Config = {}

-- Framework
Config.Framework = 'qb' -- 'esx', 'qb', 'qbox'

-- Inventory
Config.Inventory = 'ox_inventory' -- 'ox_inventory'

-- Minigame
Config.Minigame = 'ox_lib' -- 'bl_ui', 'devhub_hackingMinigames', 'devhub_minigames', 'ox_lib', 'ps-ui' (DON'T FORGET TO CONFIGURE MINIGAME SETTINGS IN SHARED/MINIGAMES.LUA)

-- Progress bar
Config.ProgressBar = 'ox_lib_bar' -- 'ox_lib_bar', 'ox_lib_circle', 'qb-core', 'unknown_ui' (UNRELEASED...), 'progressBars'

-- Notification
Config.Notify = 'ox_lib' -- 'ox_lib', 'qb-core', 'esx', 'unknown_ui' (UNRELEASED...), 'okokNotify'

-- Dispatch
Config.Dispatch = 'cd_dispatch' -- 'cd_dispatch', 'tk_dispatch', 'rcore_dispatch', 'ps-dispatch' (DON'T FORGET TO CONFIGURE DISPATCH SETTINGS IN SHARED/DISPATCH.LUA)

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

-- Radio loot settings
Config.RadioTypes = {
    ['carradio'] = { min = 1, max = 5, chance = 0.6 },
    ['carradio_cd'] = { min = 2, max = 3, chance = 0.65 },
    ['carradio_lcd'] = { min = 3, max = 5, chance = 0.8 },
}

Config.Tools = {
    ['weapon_crowbar'] = { -- IF YOU WANT TO USE WEAPON_CROWBAR AS A TOOL PLEASE READ OUR DOCUMENTAION: 
        durabilityLoss = 25 -- durability loss per use
    },
    ['screwdriver'] = {
        durabilityLoss = 50 -- durability loss per use
    },
}

Config.Animations = {
    loot = {'mini@repair', 'fixing_a_ped'},
}

Config.LootingDuration = 10000 -- in milliseconds (10 seconds)
Config.noRewardChance = 50 -- Percentage (1 = 1%, 100 = 100%)
Config.UseMinigame = true

-- Police settings
Config.PoliceRequired = 2
Config.PoliceJobName = 'police'
Config.AlertPolice = true
Config.AlertChance = 100 -- Percentage (1 = 1%, 100 = 100%)

Config.Debug = false
```

{% 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'
    elseif Config.Framework == 'qbox' and GetResourceState('qbx_core') == 'started' then
        return 'qbox'
    end
    return nil
end

function Framework:RemoveItem(source, itemName, amount)
    if Config.Inventory == 'ox_inventory' then
        return exports.ox_inventory:RemoveItem(source, itemName, amount)
    end
end

function Framework:AddItem(source, itemName, amount)
     if Config.Inventory == 'ox_inventory' then
        return exports.ox_inventory:AddItem(source, itemName, amount)
     end
end

function Framework:SetItemMetadata(source, itemName, slot, metadata)
    if Config.Inventory == 'ox_inventory' then
        return exports.ox_inventory:SetMetadata(source, slot, metadata)
    end
    return false
end

function Framework:CloseInventory()
    if Config.Inventory == 'ox_inventory' then
        return exports.ox_inventory:closeInventory()
    end
end

function Framework:GetPoliceCount()
    local policeCount = 0
    local framework = self:GetCurrent()
    
    if framework == 'qb' then
        local QBCore = exports['qb-core']:GetCoreObject()
        local players = QBCore.Functions.GetPlayers()
        
        for _, playerId in ipairs(players) do
            local Player = QBCore.Functions.GetPlayer(playerId)
            if Player and Player.PlayerData.job and Player.PlayerData.job.name == Config.PoliceJobName then
                policeCount = policeCount + 1
            end
        end
    elseif framework == 'esx' then
        local ESX = exports['es_extended']:getSharedObject()
        local players = ESX.GetPlayers()
        
        for _, playerId in ipairs(players) do
            local xPlayer = ESX.GetPlayerFromId(playerId)
            if xPlayer and xPlayer.job and xPlayer.job.name == Config.PoliceJobName then
                policeCount = policeCount + 1
            end
        end
    elseif framework == 'qbox' then
        local QBCore = exports['qbx_core']:GetCoreObject()
        local players = QBCore.Functions.GetPlayers()
        
        for _, playerId in ipairs(players) do
            local Player = QBCore.Functions.GetPlayer(playerId)
            if Player and Player.PlayerData.job and Player.PlayerData.job.name == Config.PoliceJobName then
                policeCount = policeCount + 1
            end
        end
    end
    
    return policeCount
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'}, {'w', 'a', 's', 'd'})
        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
    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 = 'Car Loot',
                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 == 'unknown_ui' and GetResourceState('unknown_ui') == 'started' then
            exports['unknown_ui']:showNotify(type, 'Car Loot', message, 5000, "#5f3dc4", "fa-solid fa-car")
        elseif Config.Notify == 'okokNotify' and GetResourceState('okokNotify') == 'started' then
            exports['okokNotify']:Alert('Car Loot', 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 == 'unknown_ui' then
            TriggerClientEvent('unknown_ui:notify:show', source, type, 'Car Loot', message, 5000, "#5f3dc4", "fa-solid fa-car")
        elseif Config.Notify == 'okokNotify' then
            TriggerClientEvent('okokNotify:Alert', source, 'Car Loot', 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('car_looting_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 == 'unknown_ui' and GetResourceState('unknown_ui') == 'started' then
            exports['unknown_ui']:showProgress({
                icon = "fa-solid fa-spinner fa-spin",
                duration = duration,
                label = label,
                canCancel = true,
                disable = {
                    move = true,
                    attack = true
                },
                anim = options.anim or {},
            })
            Wait(duration)
            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/dispatch.lua" %}

```lua
function SendPoliceAlert(coords, message)
    if not Config.AlertPolice then 
        return 
    end
    
    local locale = Locales[Config.Locale] or Locales['en']
    local dispatchMessage = message or locale['dispatch_message'] or 'Person spotted stealing radio from car!'
    
    if Config.Dispatch == 'cd_dispatch' and GetResourceState('cd_dispatch') == 'started' then
        TriggerServerEvent('cd_dispatch:AddNotification', {
            job_table = {Config.PoliceJobName or 'police'},
            coords = coords,
            title = '10-45 - Robbery',
            message = dispatchMessage,
            flash = 0,
            uniqueId = 'looting_radio_' .. GetGameTimer(),
            sound = 1,
            blip = {
                sprite = 225,
                scale = 1.0,
                colour = 1,
                flashes = false,
                text = '10-45 - Robbery',
                time = 10,
                radius = 0
            }
        })
        
    elseif Config.Dispatch == 'tk_dispatch' and GetResourceState('tk_dispatch') == 'started' then
        exports['tk_dispatch']:addCall({
            jobs = {Config.PoliceJobName or 'police'},
            coords = coords,
            title = 'Robbery',
            priority = 'Priority 2',
            message = dispatchMessage,
            removeTime = 1000 * 60 * 10,
            code = '10-45',
            blip = {
                sprite = 225,
                color = 3,
                scale = 1.0,
            }
        })
        
    elseif Config.Dispatch == 'ps-dispatch' and GetResourceState('ps-dispatch') == 'started' then
        exports['ps-dispatch']:LootingRadio()

            -- ADD THIS TO YOUR PS-DISPATCH CONFIG (MORE: https://github.com/Project-Sloth/ps-dispatch/blob/main/README.md)

            -- ['lootingRadio'] = { -- Need to match the codeName in alerts.lua
            --     radius = 0,
            --     sprite = 225,
            --     color = 3,
            --     scale = 1.0,
            --     length = 300,
            --     sound = 'Lose_1st',
            --     sound2 = 'GTAO_FM_Events_Soundset',
            --     offset = false,
            --     flash = true
            -- },

            -- ADD THIS TO YOUR PS-DISPATCH CLIENT/ALERTS.LUA

            -- local function LootingRadio()
            --     local coords = GetEntityCoords(cache.ped)
            --     local dispatchData = {
            --         message = 'Person spotted looting radio from car!', -- add this into your locale
            --         codeName = 'lootingRadio', -- this should be the same as in config.lua
            --         code = '10-45',
            --         icon = 'fa-solid fa-box',
            --         priority = 1,
            --         coords = coords,
            --         jobs = { 'leo' },
            --     }

            --     TriggerServerEvent('ps-dispatch:server:notify', dispatchData)
            -- end
            -- exports('LootingRadio', LootingRadio)
        
    elseif Config.Dispatch == 'rcore_dispatch' and GetResourceState('rcore_dispatch') == 'started' then
        local alertData = {
            code = '10-45',                         -- Alert code
            default_priority = 'medium',              -- 'low', 'medium', or 'high'
            coords = coords,                        -- Alert location
            job = Config.PoliceJobName or 'police', -- Job(s) to receive alert (string or table)
            text = dispatchMessage,                 -- Alert description
            type = 'alerts',                        -- Alert type for statistics
            blip_time = 500,                        -- Optional: Blip fade time (seconds)
            -- image = 'url_to_image.jpg',          -- Optional: Image URL
            -- custom_sound = 'url_to_sound.mp3',   -- Optional: Custom sound URL
            blip = {                                -- Optional: Custom blip settings
                sprite = 225,
                colour = 3,
                scale = 1.0,
                text = '10-45 - Robbery',
                flashes = false,
                radius = 0,
            }
        }

        TriggerServerEvent('rcore_dispatch:server:sendAlert', alertData)
        
    else
        Notify(0, dispatchMessage, 'info')
    end
end
```

{% endtab %}
{% endtabs %}

## Locales

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

```lua
Locales = {
    ['en'] = {
        ['must_be_in_vehicle'] = 'You must be in a vehicle!',
        ['already_looted'] = 'This vehicle has already been looted!',
        ['must_be_same_vehicle'] = 'You must be in the same vehicle!',
        ['looting_error'] = 'Error: Could not get looting data',
        ['failed_to_break'] = 'Failed to break the radio.',
        ['looting_radio'] = 'Breaking radio...',
        ['looting_cancelled'] = 'Looting cancelled.',
        ['found_nothing'] = 'Found nothing in the vehicle.',
        ['found_radio'] = 'Found a radio!',
        ['tool_broken'] = 'Your %s broke!',
        ['not_enough_police'] = 'Not enough police officers. Required: %s',
        ['dispatch_message'] = 'Person spotted stealing radio from car!',
    },
    
    ['lt'] = {
        ['must_be_in_vehicle'] = 'Turi būti transporto priemonėje!',
        ['already_looted'] = 'Ši tr. priemonė jau buvo išplėšta!',
        ['must_be_same_vehicle'] = 'Turi būti toje pačioje transporto priemonėje!',
        ['looting_error'] = 'Klaida: Nepavyko gauti looting duomenų',
        ['failed_to_break'] = 'Nepavyko išlaužti magės.',
        ['looting_radio'] = 'Lupama magė...',
        ['looting_cancelled'] = 'Laužimas atšauktas.',
        ['found_nothing'] = 'Mašinoje nieko neradai.',
        ['found_radio'] = 'Radai magių!',
        ['tool_broken'] = 'Tavo %s sulūžo!',
        ['not_enough_police'] = 'Nepakanka pareigūnų. Reikia: %s',
        ['dispatch_message'] = 'Pastebėtas asmuo, vogiantis radiją iš mašinos!',
    }
}
```

{% 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_carloot.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.
