📦Installation

1. Add Safe Items to Core

Add the following entries to:

core > shared.gta5.lua These define the usable safe items and their metadata.

["largesafe"] = {
    ["label"] = "Large Safe",
    ["weight"] = 30000,
    ["type"] = "item",
    ["image"] = "largesafe.webp",
    ["unique"] = true,
    ["stackable"] = true,
    ["useable"] = true,
    ["shouldClose"] = true,
    ["description"] = "What am I gonna store here..",
    ["metadata"] = {
        password = 0,
        stashId = 0,
        destroyed = 0
    }
},

["smallsafe"] = {
    ["label"] = "Small Safe",
    ["weight"] = 30000,
    ["type"] = "item",
    ["image"] = "smallsafe.webp",
    ["unique"] = true,
    ["stackable"] = true,
    ["useable"] = true,
    ["shouldClose"] = true,
    ["description"] = "What am I gonna store here..",
    ["metadata"] = {
        password = 0,
        stashId = 0,
        destroyed = 0
    }
},

Be sure to also add both included .webp images to your inventory image folder.


2. Add Interaction Types to Core Game

Add the following to:

core_game > config.lua > Config.InteractionTypes These define how the safes behave when placed or interacted with.

['largesafe'] = {
    PromptName = 'Large Safe',
    PromptDescription = 'What can I store here?...',
    PromptIcon = 'fa-solid fa-vault',
    PromptComplete = function(objConf, obj)
        TaskTurnPedToFaceEntity(playerPedId, obj, 2000, 0.0, 0.0, 0.0)
        Citizen.Wait(250)
        TriggerEvent('client:playersafes:getSafe', source)
    end,
    Object = `m23_2_prop_m32_safe_01a`,
    UseItemMetadata = true,
    InteractDist = 1.3,
    ReturnItem = 'largesafe',
    StoreMetadataOnReturn = true,
    Offset = vector4(0.0, 0.0, -1.0, 0.0),
    LimitUse = false,
},

['smallsafe'] = {
    PromptName = 'Small Safe',
    PromptDescription = 'What can I store here?...',
    PromptIcon = 'fa-solid fa-vault',
    PromptComplete = function(objConf, obj)
        TaskTurnPedToFaceEntity(playerPedId, obj, 2000, 0.0, 0.0, 0.0)
        Citizen.Wait(250)
        TriggerEvent('client:playersafes:getSafe', source)
    end,
    Object = `prop_ld_int_safe_01`,
    UseItemMetadata = true,
    InteractDist = 1.3,
    ReturnItem = 'smallsafe',
    StoreMetadataOnReturn = true,
    Offset = vector4(0.0, 0.0, -1.0, 0.0),
    LimitUse = false,
},

This enables the pickup system and returns safes to the player with metadata preserved.


3. Add Radial Menu Option

Add the following entry to:

radialmenu > config.lua > newSubMenus

['general:pickupPlayerSafe'] = {
    title = 'Pickup Safe',
    icon = 'vault',
    iconCategory = 'solid',
    functionName = 'playersafes:client:pickupClosestSafe',
    enableMenu = function()
        return not isDead and (
            exports['core_game']:CanPickupInteractionObject('smallsafe')
            or exports['core_game']:CanPickupInteractionObject('largesafe')
        )
    end,
},

Then add 'general:pickupPlayerSafe' to your general rootMenuConfig to make it appear in the radial menu.


4. Ensure the Resource

If needed by your server structure, add this line to your:

server.cfg / resources.cfg
ensure playersafes

5. Configure the Script

Open the script’s config.lua and adjust to your liking.

All safes use metadata, allowing each placed safe to be fully persistent and unique.


🎉 Installation Complete

Once installed:

  • Players can place Small and Large Safes as world objects

  • Safe metadata (password, stashId, destroyed state) persists

  • Safes can be picked up using the radial menu

  • Core interactions will handle storing and retrieving safes correctly

Last updated