bridge/inventory.lua

-- ==========================================
-- INVENTORY BRIDGE  (server-side only)
-- ==========================================
-- Auto-detects the active inventory resource and provides
-- a unified API so the main script never calls inventory
-- exports directly.
--
-- Supported inventories:
--   1. ox_inventory
--   2. tgiann-inventory
--
-- NOTE: qb-inventory is NOT supported — it does not reliably
-- persist item metadata which is required for drone serials,
-- battery charge levels, skin images, etc.
--
-- Global table:  Inv
--
-- Normalised item format returned by  Inv.GetItem / Inv.GetItems:
--   { name = 'item_name', amount = 1, info = { ... }, slot = 3 }
-- ==========================================

Inv = {}

-- ==========================================
-- AUTO-DETECT
-- ==========================================
local invType = Config.Setup and Config.Setup.inventory or 'ox'

-- Validate the configured inventory is actually running
local invResourceMap = {
    ox     = 'ox_inventory',
    tgiann = 'tgiann-inventory',
}

if invType == 'qb' then
    print('[fiveous_drones] ^1ERROR: qb-inventory is not supported — it does not reliably handle item metadata.^0')
    print('[fiveous_drones] ^1  → Please switch to ox_inventory or tgiann-inventory and set Config.Setup.inventory accordingly.^0')
end

local expectedResource = invResourceMap[invType]
if not expectedResource then
    -- Try auto-detect if invalid/unsupported value
    if GetResourceState('ox_inventory') == 'started' then
        invType = 'ox'
        expectedResource = 'ox_inventory'
        print('[fiveous_drones] ^3Auto-detected ox_inventory^0')
    elseif GetResourceState('tgiann-inventory') == 'started' then
        invType = 'tgiann'
        expectedResource = 'tgiann-inventory'
        print('[fiveous_drones] ^3Auto-detected tgiann-inventory^0')
    else
        print('[fiveous_drones] ^1ERROR: No supported inventory found! ox_inventory or tgiann-inventory is required.^0')
        print('[fiveous_drones] ^1  → This script requires item metadata support for drone serials, battery charge, etc.^0')
    end
elseif GetResourceState(expectedResource) ~= 'started' then
    print('[fiveous_drones] ^1WARNING: Config.Setup.inventory = "' .. invType .. '" but ' .. expectedResource .. ' is not started!^0')
    -- Try the other supported inventory
    if invType == 'ox' and GetResourceState('tgiann-inventory') == 'started' then
        invType = 'tgiann'
        print('[fiveous_drones] ^3Falling back to tgiann-inventory^0')
    elseif invType == 'tgiann' and GetResourceState('ox_inventory') == 'started' then
        invType = 'ox'
        print('[fiveous_drones] ^3Falling back to ox_inventory^0')
    end
end

print('[fiveous_drones] Inventory bridge → ' .. invType)

-- ==========================================
-- OX_INVENTORY
-- ==========================================
if invType == 'ox' then
    local ox = exports.ox_inventory

    --- Normalise an ox item → unified format (.info, .amount)
    local function norm(item)
        if not item then return nil end
        return {
            name     = item.name,
            amount   = item.count or item.amount or 1,
            info     = item.metadata or {},
            metadata = item.metadata or {},
            slot     = item.slot,
            label    = item.label,
        }
    end

    function Inv.AddItem(src, name, count, metadata, slot)
        local ok = ox:AddItem(src, name, count or 1, metadata, slot)
        return ok and true or false
    end

    function Inv.RemoveItem(src, name, count, slot)
        local ok = ox:RemoveItem(src, name, count or 1, nil, slot)
        return ok and true or false
    end

    function Inv.GetItem(src, name)
        local items = ox:Search(src, 'slots', name)
        if not items or #items == 0 then return nil end
        return norm(items[1])
    end

    function Inv.GetItems(src)
        local raw = ox:GetInventoryItems(src)
        if not raw then return {} end
        local out = {}
        for _, item in pairs(raw) do
            if item and item.slot then
                out[item.slot] = norm(item)
            end
        end
        return out
    end

    function Inv.HasItem(src, name, count)
        local result = ox:Search(src, 'count', name)
        return result and result >= (count or 1)
    end

    function Inv.RegisterUsableItem(name, cb)
        -- Usable items are always registered through the framework, not the inventory.
        -- QBCore / QBox handle this; ox_inventory does not provide its own export for it.
        if Config.Setup and Config.Setup.framework == 'qbcore' then
            local QBCore = exports['qb-core']:GetCoreObject()
            QBCore.Functions.CreateUseableItem(name, function(source, item)
                cb(source, norm(item) or item)
            end)
        end
    end

-- ==========================================
-- TGIANN-INVENTORY
-- ==========================================
elseif invType == 'tgiann' then
    local tg = exports['tgiann-inventory']

    --- Normalise tgiann item → unified format
    local function norm(item)
        if not item then return nil end
        return {
            name     = item.name,
            amount   = item.amount or item.count or 1,
            info     = item.info or item.metadata or {},
            metadata = item.info or item.metadata or {},
            slot     = item.slot,
            label    = item.label,
        }
    end

    function Inv.AddItem(src, name, count, metadata, slot)
        local ok = tg:AddItem(src, name, count or 1, slot, metadata)
        return ok and true or false
    end

    function Inv.RemoveItem(src, name, count, slot)
        local ok = tg:RemoveItem(src, name, count or 1, slot)
        return ok and true or false
    end

    function Inv.GetItem(src, name)
        local item = tg:GetItemByName(src, name)
        return norm(item)
    end

    function Inv.GetItems(src)
        local raw = tg:GetPlayerItems(src)
        if not raw then return {} end
        local out = {}
        for _, item in pairs(raw) do
            if item and item.slot then
                out[item.slot] = norm(item)
            end
        end
        return out
    end

    function Inv.HasItem(src, name, count)
        local item = Inv.GetItem(src, name)
        return item ~= nil and (item.amount or 0) >= (count or 1)
    end

    function Inv.RegisterUsableItem(name, cb)
        -- tgiann typically supports QBCore's CreateUseableItem too,
        -- but we prefer the direct export for safety
        local QBCore = exports['qb-core']:GetCoreObject()
        QBCore.Functions.CreateUseableItem(name, function(source, item)
            cb(source, norm(item) or item)
        end)
    end
end

-- ==========================================
-- SHARED HELPERS (work with any backend)
-- ==========================================

--- Convenience: get item metadata table regardless of key name
--- Works on raw items from any backend
--- @param item table
--- @return table metadata
function Inv.GetItemInfo(item)
    if not item then return {} end
    return item.info or item.metadata or {}
end

Last updated