RX Scripts Logo
Crates & Storages

Configurables

All config & open sourced files included within this script.

Config Files

--[[
BY RX Scripts © rxscripts.xyz
--]]

Config = {}

Config.Locale = 'en'
Config.MaxStorages = 1 -- How many storages can a player place
Config.PlaceOnGround = true -- Place objects on ground properly (can be disabled for other props that need a bit different placing)

Config.SearchAnim = {
    dict = "mini@repair",
    anim = "fixing_a_player",
}

Config.LockItem = 'codelock'
Config.LockProp = 'codelock'

Config.StorageCrates = {
    {
        label = 'Wooden Storage Big',
        prop = "boxonebig",
        placeItem = "boxonebig",
        slots = 12,
        icon = "fas fa-box-open",
        iconColor = "#ff0000",
        inventoryStash = false, -- If true, it will open a stash from your inventory instead of our own storage system, picking up the storage will remove all items inside!
    },
    {
        label = 'Wooden Storage Small',
        prop = "boxonesmall",
        placeItem = "boxonesmall",
        slots = 6,
        icon = "fas fa-box-open",
        iconColor = "#ff0000",
        inventoryStash = false,
    },
    {
        label = 'Wooden Storage Small',
        prop = "boxtwosmall",
        placeItem = "boxtwosmall",
        slots = 6,
        icon = "fas fa-box-open",
        iconColor = "#ff0000",
        inventoryStash = false,
    },
    {
        label = 'Wooden Storage Big',
        prop = "boxtwobig",
        placeItem = "boxtwobig",
        slots = 12,
        icon = "fas fa-box-open",
        iconColor = "#ff0000",
        inventoryStash = false,
    },
    {
        label = 'Wooden Storage Big',
        prop = "boxtreebig",
        placeItem = "boxtreebig",
        slots = 12,
        icon = "fas fa-box-open",
        iconColor = "#ff0000",
        inventoryStash = false,
    },
}

Config.StorageStackLimits = {
    ['WEAPON_ASSAULTRIFLE'] = 1,
}

Config.RespawnCrates = {
    {
        label = 'Supply Crate',
        prop = "boxcrate",
        locations = {
            vector4(3053.4177, -4725.9736, 15.2616, 18.8735),
            vector4(3046.7397, -4727.8799, 15.2613, 45.3019),
        },
        spawning = {
            limit = 1, -- How many crates can be spawned at once from locations above
            different = false, -- If true, will spawn on different location then previous
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 1, -- Minutes, set to false to disable
        spawnDirect = false, -- Spawn crate directly after script start
        hacking = {
            enabled = true,
            time = 1, -- Minutes
            minigame = true,
            item = 'redkeycard', -- false to disable
            removeItem = true, -- Remove item from player after hacking
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 20 },
                { item = 'at_flashlight', amount = 1, chance = 30 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 30 },
                { item = 'armour', amount = 1, chance = 20 },
            }
        },
        blip = { -- Remove this if u don't want a blip on crate spawn
            sprite = 478,
            color = 1,
            display = 4,
            scale = 1.0,
            shortRange = true,
            label = 'Supply Crate',
        }
    },
    {
        label = 'Ammo Crate',
        prop = "crateammo",
        locations = {
            vector4(3046.7034, -4706.5747, 15.2616, 288.2096),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 1, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = true,
            time = 1, -- Minutes
            minigame = true,
            item = 'bluekeycard', -- Set to false to disable
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 25 },
                { item = 'ammo-shotgun', amount = function() return math.random(20, 50) end, chance = 25 },
                { item = 'ammo-9', amount = function() return math.random(20, 50) end, chance = 25 },
                { item = 'ammo-musket', amount = function() return math.random(20, 50) end, chance = 25 },
            }
        }
    },
    {
        label = 'Bomb Crate',
        prop = "cratec4",
        locations = {
            vector4(3047.8506, -4709.7803, 15.2616, 281.4715),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 30, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = true,
            time = 5, -- Minutes
            minigame = false,
            item = false,
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 3 },
                { item = 'WEAPON_PISTOL', amount = 1, chance = 8 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 30 },
                { item = 'water', amount = function() return math.random(3, 10) end, chance = 59 },
            }
        }
    },
    {
        label = 'Basic Crate',
        prop = "lootcratebasic",
        locations = {
            vector4(3049.0000, -4713.0693, 15.2616, 287.8040),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 30, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = false,
            time = 15, -- Minutes
            minigame = true,
            item = false,
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 3 },
                { item = 'WEAPON_PISTOL', amount = 1, chance = 8 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 30 },
                { item = 'water', amount = function() return math.random(3, 10) end, chance = 59 },
            }
        },
        blip = { -- remove this if u don't want a blip on crate spawn
            sprite = 478,
            color = 1,
            display = 4,
            scale = 1.0,
            shortRange = true,
            label = 'Basic Crate',
        }
    },
    {
        label = 'Food Crate',
        prop = "lootcratefood",
        locations = {
            vector4(3050.1790, -4716.1348, 15.2616, 270.5500),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 30, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = false,
            time = 15, -- Minutes
            minigame = true,
            item = false,
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 3 },
                { item = 'WEAPON_PISTOL', amount = 1, chance = 8 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 30 },
                { item = 'water', amount = function() return math.random(3, 10) end, chance = 59 },
            }
        }
    },
    {
        label = 'Medical Crate',
        prop = "lootcratemedi",
        locations = {
            vector4(3051.3313, -4718.6816, 15.2616, 249.9581),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 30, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = false,
            time = 15, -- Minutes
            minigame = true,
            item = false,
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 3 },
                { item = 'WEAPON_PISTOL', amount = 1, chance = 8 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 30 },
                { item = 'water', amount = function() return math.random(3, 10) end, chance = 59 },
            }
        }
    },
    {
        label = 'Elite Crate',
        prop = "crateelite",
        locations = {
            vector4(3052.4419, -4721.6367, 15.2616, 110.0965),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 30, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = false,
            time = 15, -- Minutes
            minigame = true,
            item = false,
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 5 },
                { item = 'WEAPON_PISTOL', amount = 1, chance = 10 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 25 },
                { item = 'ammo-shotgun', amount = function() return math.random(20, 50) end, chance = 25 },
                { item = 'ammo-9', amount = function() return math.random(20, 50) end, chance = 20 },
                { item = 'ammo-musket', amount = function() return math.random(20, 50) end, chance = 15 },
            }
        }
    },
    {
        label = 'Military Crate',
        prop = "militarycrates",
        locations = {
            vector4(3050.1714, -4726.9888, 15.2614, 18.4443),
        },
        slots = 12,
        icon = "fas fa-box-open", -- https://fontawesome.com/icons
        iconColor = "#ff0000",
        respawnTime = 30, -- Minutes, set to false to disable
        spawnDirect = true, -- Spawn crate directly after script start
        hacking = {
            enabled = false,
            time = 15, -- Minutes
            minigame = true,
            item = false,
        },
        contents = {
            itemsAmount = function() return math.random(3, 6) end,
            lootables = { -- Make sure all chances add up to 100
                { item = 'WEAPON_ASSAULTRIFLE', amount = 1, chance = 3 },
                { item = 'WEAPON_PISTOL', amount = 1, chance = 8 },
                { item = 'ammo-rifle2', amount = function() return math.random(20, 50) end, chance = 30 },
                { item = 'water', amount = function() return math.random(3, 10) end, chance = 59 },
            }
        }
    },
}

--[[
    INITIALIZATION SECTION

    ONLY UNCOMMENT/CHANGE THIS IF YOU HAVE RENAMED SCRIPTS SUCH AS FRAMEWORK, TARGET, INVENTORY ETC
    RENAME THE SCRIPT NAME TO THE NEW NAME
--]]
-- ESX = 'es_extended'
-- QB = 'qb-core'
-- OXTarget = 'ox_target'
-- QBTarget = 'qb-target'
-- OXINV = 'ox_inventory'
-- QBInv = 'qb-inventory'
-- QSInv = 'qs-inventory'
-- PSInv = 'ps-inventory'
IgnoreResourceNotFoundErrors = false

Opensource Files

All script-related open source code is contained within these files. Third-party components, including frameworks, inventory systems, and other external code, are separately maintained & open sourced in our fmLib repository.
--[[
BY RX Scripts © rxscripts.xyz
--]]

-- return true if minigame has been succeeded, false if not
function HackMinigame(crate)
    return lib.skillCheck({'easy', 'easy', 'easy'}, {'w', 'a', 's', 'd'})

    -- local returnable = nil
    -- TriggerEvent("mhacking:show")
    -- TriggerEvent("mhacking:start", 7, 35, function(success, timeremaining)
    --     if success then
    --         returnable = true
    --     else
    --         returnable = false
    --     end

    --     TriggerEvent("mhacking:hide")
    -- end)

    -- while returnable == nil do
    --     Wait(0)
    -- end

    -- return returnable
end

function InitCrateTarget(entity, key, crate)
    if Target == 'OX' then
        exports.ox_target:addLocalEntity(entity, {
            {
                name = 'opencrate',
                label = 'Open '..crate.label,
                icon = crate.icon,
                iconColor = crate.iconColor,
                distance = 2.5,
                canInteract = function()
                    if crate.hacking and crate.hacking.enabled then
                        return Crates[key].hacked
                    else
                        return true
                    end
                end,
                onSelect = function(data)
                    OpenCrate(key, crate, entity, false)
                end
            },
            {
                name = 'pickupstorage',
                label = 'Pickup '..crate.label,
                icon = crate.icon,
                iconColor = crate.iconColor,
                distance = 2.5,
                canInteract = function()
                    return crate.storage
                end,
                onSelect = function(data)
                    PickupStorage(key, crate)
                end
            },
            {
                name = 'hackcrate',
                label = 'Hack '..crate.label,
                icon = "fas fa-laptop",
                iconColor = "#f3f315",
                distance = 2.5,
                canInteract = function()
                    if crate.hacking and crate.hacking.enabled and Crates[key].hackTimeLeft == nil then
                        return not Crates[key].hacked
                    else
                        return false
                    end
                end,
                onSelect = function(data)
                    HackCrate(key, crate, entity)
                end
            },
            {
                name = 'addlock',
                label = 'Add Lock',
                icon = "fas fa-lock",
                iconColor = "#3f3f3f",
                distance = 2.5,
                canInteract = function()
                    if crate.storage and not crate.lock and HasItem(Config.LockItem) then
                        return true
                    else
                        return false
                    end
                end,
                onSelect = function(data)
                    AddLock(key)
                end
            },
            {
                name = 'removelock',
                label = 'Remove Lock',
                icon = "fas fa-lock-open",
                iconColor = "#3f3f3f",
                distance = 2.5,
                canInteract = function()
                    if crate.storage and crate.lock then
                        return true
                    else
                        return false
                    end
                end,
                onSelect = function()
                    RemoveLock(key, crate)
                end,
            }
        })
    elseif Target == 'QB' then
        exports['qb-target']:AddTargetEntity(entity, {
            options = {
                {
                    num = 1,
                    label = 'Open '..crate.label,
                    icon = crate.icon,
                    targeticon = crate.icon,
                    canInteract = function()
                        if crate.hacking and crate.hacking.enabled then
                            return Crates[key].hacked
                        else
                            return true
                        end
                    end,
                    action = function()
                        OpenCrate(key, crate, entity, false)
                    end
                },
                {
                    num = 2,
                    label = 'Pickup '..crate.label,
                    icon = crate.icon,
                    targeticon = crate.icon,
                    canInteract = function()
                        return crate.storage
                    end,
                    action = function()
                        PickupStorage(key, crate)
                    end
                },
                {
                    num = 3,
                    label = 'Hack '..crate.label,
                    icon = "fas fa-laptop",
                    targeticon = "fas fa-laptop",
                    canInteract = function()
                        if crate.hacking and crate.hacking.enabled and Crates[key].hackTimeLeft == nil then
                            return not Crates[key].hacked
                        else
                            return false
                        end
                    end,
                    action = function()
                        HackCrate(key, crate, entity)
                    end
                },
                {
                    num = 4,
                    label = 'Add Lock',
                    icon = "fas fa-lock",
                    targeticon = "fas fa-lock",
                    canInteract = function()
                        if crate.storage and not crate.lock and HasItem(Config.LockItem) then
                            return true
                        else
                            return false
                        end
                    end,
                    action = function()
                        AddLock(key)
                    end
                },
                {
                    num = 5,
                    label = 'Remove Lock',
                    icon = "fas fa-lock-open",
                    targeticon = "fas fa-lock-open",
                    canInteract = function()
                        if crate.storage and crate.lock then
                            return true
                        else
                            return false
                        end
                    end,
                    action = function()
                        RemoveLock(key, crate)
                    end
                }
            },
            distance = 2.5,
        })
    end
end

FM = exports['fmLib']:new()
---@return string | nil
function InitLockDialog(correctPin)
    local opts = {
        title = 'Code Lock',
        subtitle = 'Enter the lock code.',
        maxNumbers = 4,
        hidden = true,
    }

    if correctPin then
        opts.reactiveUI = {
            correctPin = correctPin,
        }
    end

    local pin = FM.pin.open(opts)

    -- local input = lib.inputDialog('Insert Code', {
    --     {
    --         type = 'slider',
    --         label = 'Number 1',
    --         icon = 'lock',
    --         required = true,
    --         default = 0,
    --         min = 0,
    --         max = 9,
    --         step = 1,
    --     },
    --     {
    --         type = 'slider',
    --         label = 'Number 2',
    --         icon = 'lock',
    --         required = true,
    --         default = 0,
    --         min = 0,
    --         max = 9,
    --         step = 1,
    --     },
    --     {
    --         type = 'slider',
    --         label = 'Number 3',
    --         icon = 'lock',
    --         required = true,
    --         default = 0,
    --         min = 0,
    --         max = 9,
    --         step = 1,
    --     },
    --     {
    --         type = 'slider',
    --         label = 'Number 4',
    --         icon = 'lock',
    --         required = true,
    --         default = 0,
    --         min = 0,
    --         max = 9,
    --         step = 1,
    --     }
    -- })

    -- if input then
    --     return input[1] .. input[2] .. input[3] .. input[4]
    -- end

    return pin
end

function Notify(msg, type)
    if Core == 'ESX' then
        CoreObj.ShowNotification(msg, type)
    elseif Core == 'QB' then
        CoreObj.Functions.Notify(msg, type)
    end
end

function HasItem(item)
    if Core == 'ESX' then
        local has = CoreObj.SearchInventory(item, 1)
        return has and has > 0
    elseif Core == 'QB' then
        return CoreObj.Functions.HasItem(item)
    end

    return false
end

function OpenStash(stashId, slots)
    stashId = stashId:gsub('-', '_')

    if OXINV then
        OXINV:openInventory('stash', stashId)
    elseif QBInv or QSInv or PSInv then
        TriggerServerEvent('inventory:server:OpenInventory', 'stash', stashId, {
            maxweight = 100,
            slots = slots,
        })
        TriggerEvent('inventory:client:SetCurrentStash', stashId)
    else
        Error("No inventory found for opening stash (client/opensource.lua:OpenStash)")
    end
end

RegisterNetEvent('crates:onCrateOpened', function(crate, data)
    -- Here you can do something when a crate is opened
end)