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

# Editable Files

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

```lua
function notify(message)
	ESX.ShowNotification(getMessage(message))
end

function getMessage(message)
    return Savana.Locale[Savana.Language][message]
end

function DrawText3D(x, y, z, text)
    SetTextScale(0.35, 0.35)
    SetTextFont(4)
    SetTextProportional(1)
    SetTextColour(255, 255, 255, 215)
    SetTextEntry('STRING')
    SetTextCentre(true)
    AddTextComponentString(text)
    SetDrawOrigin(x, y, z, 0)
    DrawText(0.0, 0.0)
    local factor = (string.len(text)) / 370
    DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 0, 0, 0, 75)
    ClearDrawOrigin()
end
```

{% endtab %}

{% tab title="utils.lua" %}

```lua
k = {}

Server = IsDuplicityVersion()

if Server then 
    k.RegisterEvent = function (name, fn)
        RegisterNetEvent(('savana-events:%s'):format(name), function (id, ...)
            local src = source
            TriggerClientEvent(('savana-events:%s:%s'):format(name, id), src, fn(src, ...))
        end)
    end
    
    k.getMoney = function(src, type)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            return player.Functions.GetMoney(type)
        else
            local player = fw.GetPlayerFromId(src)
            if type == "cash" then
                type = "money"
            end
            return player.getAccount(type).money
        end
    end

    k.hasEnoughMoney = function(src, val, type)
        local money
        if cfg.core == "qb" then
            money = k.getMoney(src, type or "cash")
        else
            money = k.getMoney(src, type or "money")
        end
        if money >= val then
            return true
        else
            return false
        end
    end

    k.removeMoney = function(src, val, type)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            return player.Functions.RemoveMoney(type, val)
        else
            local player = fw.GetPlayerFromId(src)
            if type == "cash" then
                type = "money"
            end
            return player.removeAccountMoney(type, val)
        end
    end

    k.addMoney = function(src, val, type)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            return player.Functions.AddMoney(type, val)
        else
            local player = fw.GetPlayerFromId(src)
            if type == "cash" then
                type = "money"
            end
            return player.addAccountMoney(type, val)
        end
    end

    k.getPlayerId = function(src)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            return player.PlayerData.citizenid
        else
            local player = fw.GetPlayerFromId(src)
            return player.getIdentifier()
        end
    end

    k.getPlayerName = function(id)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayerByCitizenId(id)
            if not player then
                player = fw.Functions.GetPlayer(id)
            end

            if player then
                return player.PlayerData.charinfo.firstname .. " " .. player.PlayerData.charinfo.lastname
            end
        else
            local player = fw.GetPlayerFromIdentifier(id)

            if not player then
                player = fw.GetPlayerFromId(id)
            end

            if player then
                return player.getName()
            else
                return "0"
            end
        end
    end

    k.getInventoryItems = function(src)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            if player then
                return player.PlayerData.items
            end
        else
            local player = fw.GetPlayerFromId(src)
            if player then
                return player.inventory
            end
        end
    end


    k.removeInventoryItem = function(src, item, qty)
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            if player.Functions.RemoveItem(item, qty) then
                return true
            end
        else
            local player = fw.GetPlayerFromId(src)
            if player.removeInventoryItem(item, qty) then
                return true
            end
        end

        return false
    end

    k.addInventoryItem = function(src, item, qty) 
        if cfg.core == "qb" then
            local player = fw.Functions.GetPlayer(src)
            return player.Functions.AddItem(item, qty) 
        else
            local player = fw.GetPlayerFromId(src)
            return player.addInventoryItem(item, qty)
        end
    end
end

if not Server then
    k.addBlip = function(coords, sprite, scale, color, str, cb)
        local blip = AddBlipForCoord(coords)
        SetBlipSprite(blip, sprite)
        SetBlipColour(blip, color)
        SetBlipAsShortRange(blip, true)
        SetBlipScale(blip, scale)
        BeginTextCommandSetBlipName('STRING')
        AddTextComponentSubstringPlayerName(str)
        EndTextCommandSetBlipName(blip)
        if cb then cb(blip) else return blip end
    end

    function DrawText3D(x, y, z, text)
        SetTextScale(0.35, 0.35)
        SetTextFont(4)
        SetTextProportional(1)
        SetTextColour(255, 255, 255, 215)
        SetTextEntry('STRING')
        SetTextCentre(true)
        AddTextComponentString(text)
        SetDrawOrigin(x, y, z, 0)
        DrawText(0.0, 0.0)
        local factor = (string.len(text)) / 370
        DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 0, 0, 0, 75)
        ClearDrawOrigin()
    end
    
    local target = nil
    
    Citizen.CreateThread(function()
        local sleep = 0
        if not cfg["target"] then
            while true do
                local market = getNearMarkets()
                if market then
                    sleep = 0
                    local shopCoord = cfg["markets"][target]["pedCoords"]
                    if target ~= "Whosaller" then
                        DrawText3D(shopCoord.x, shopCoord.y, shopCoord.z+1, cfg["lang"]["openMarket"])
                    else
                        DrawText3D(shopCoord.x, shopCoord.y, shopCoord.z+1, cfg["lang"]["openWhosaller"])
                    end
    
                    if IsControlJustReleased(0, 38) then
                        OpenMarket(target)
                    end
                else
                    sleep = 1000
                    target = nil
                end    
                Citizen.Wait(sleep)
            end
        end
    end)

    function getNearMarkets()
        for k,v in pairs(cfg["markets"]) do
            local marketCoord = vector3(v.coords.x, v.coords.y, v.coords.z)
            local myCoord = GetEntityCoords(PlayerPedId())
            if (GetDistanceBetweenCoords(marketCoord, myCoord, true) < 3.0) then
                target = k
                return true
            end
        end
        return false
    end


    k.getPlayerJob = function()
        if cfg.core == "qb" then
            return fw.Functions.GetPlayerData().job 
        else
            return fw.GetPlayerData().job.name
        end
    end

    k.notify = function(text, type)
        if cfg.core == "qb" then
            TriggerEvent('QBCore:Notify', text, type)
        else
            -- print(fw)
            fw.ShowNotification(text, true) 
        end
    end

    local id = GetPlayerServerId(PlayerId())

    k.TriggerServerEvent = function (name, ...)
        local p = promise.new()

		SetTimeout(5000, function()
			p:reject({err="Event not Found!"})
		end)

        local handler = RegisterNetEvent(('savana-events:%s:%s'):format(name, id), function (...)
            p:resolve({...})
        end)

        TriggerServerEvent(('savana-events:%s'):format(name), id, ...)

        local data = Citizen.Await(p)
        RemoveEventHandler(handler)
        return table.unpack(data)
    end
end
```

{% endtab %}

{% tab title="database.lua" %}

```lua
Database = {}

---@param text string
---@param ... unknown
Database.Debug = function(text, ...)
    -- print(('^1[DEBUG]^7 %s^7'):format((text):format(...)))
end

---@param callback function
---@param ... unknown
Database.TriggerCallback = function(callback, ...)
    if callback then
        callback(...)
    end
end

---@param collection string
---@param table table
---@param callback function
Database.InsertTableToCollection = function(collection, table, callback)
    local Collection = LoadResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection))
    if not Collection then
        Database.Debug('Collection %s does not exist.', collection)
    else
        local CollectionData = json.decode(Collection)
        CollectionData[#CollectionData+1] = table
        SaveResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection), json.encode(CollectionData, {indent = true}))
    end
end

---@param collection string
---@param query table
---@param callback function
Database.DeleteTableToCollection = function(collection, query, callback)
    local Collection = LoadResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection))
    if not Collection then
        Database.Debug('Collection %s does not exist.', collection)
        Database.TriggerCallback(callback, false)
    else
        local CollectionData = json.decode(Collection)
        if query then
            local QueryData = {}
            for k, v in pairs(CollectionData) do
                local retval = true
                for _, i in pairs(query) do
                    if v[_] ~= i then
                        retval = false
                    end
                end
                if not retval then
                    QueryData[#QueryData+1] = v
                end
            end
            ---@diagnostic disable-next-line: missing-parameter
            SaveResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection), json.encode(QueryData, {indent = true}))
            Database.TriggerCallback(callback, true)
        else
            Database.Debug('Query is empty.', collection)
            Database.TriggerCallback(callback, false)
        end
    end
end

---@param collection string
---@param query table
---@param update table
---@param callback function
Database.UpdateTableToCollection = function(collection, query, update, callback)
    local Collection = LoadResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection))
    if not Collection then
        Database.Debug('Collection %s does not exist.', collection)
    else
        local CollectionData = json.decode(Collection)
        if query then
            for k, v in pairs(CollectionData) do
                local retval = true
                for _, i in pairs(query) do
                    if v[_] ~= i then
                        retval = false
                    end
                end
                if retval then
                    for _, i in pairs(update) do
                        v[_] = i
                    end
                end
            end
            ---@diagnostic disable-next-line: missing-parameter
            SaveResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection), json.encode(CollectionData, {indent = true}))
        else
            Database.Debug('Query is empty.', collection)
        end
    end
end

---@param collection string
---@param query table
Database.GetTableToCollection = function(collection, query)
    local promise = promise.new()

    local Collection = LoadResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection))
    if not Collection then
        Database.Debug('Collection %s does not exist.', collection)
        promise:reject({ err = ('Collection %s does not exist.'):format(collection) })
    else
        local QueryData = {}
        local CollectionData = json.decode(Collection)
        if query then
            for k, v in pairs(CollectionData) do
                local retval = true
                for _, i in pairs(query) do
                    if v[_] ~= i then
                        retval = false
                    end
                end
                if retval then
                    QueryData[#QueryData+1] = v
                end
            end
        else
            QueryData = CollectionData
        end
        -- print(QueryData)
        promise:resolve(QueryData)
    end

    local response = Citizen.Await(promise)
    return response
end

---@param collection string
---@param query table
Database.GetCollection = function(collection)
    local promise = promise.new()

    local Collection = LoadResourceFile(GetCurrentResourceName(), ('collections/%s.json'):format(collection))
    if not Collection then
        Database.Debug('Collection %s does not exist.', collection)
        promise:reject({ err = ('Collection %s does not exist.'):format(collection) })
    else
        local CollectionData = json.decode(Collection)
        promise:resolve(CollectionData)
    end

    local response = Citizen.Await(promise)
    return response
end
```

{% 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://savana.gitbook.io/savanascripts/scripts/ownable-shops/editable-files.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.
