From 981563df6a2459f6b0c8bd7997ede0eda527d54d Mon Sep 17 00:00:00 2001 From: Jim Shield Date: Wed, 13 Aug 2025 18:11:39 +0100 Subject: [PATCH] Compile inventory functions into one file This removes redundant inventory files and compiles all of the functions into one This allows easier editing and control per inventory and hopefully better optimization, readability and easier addition of support for new inventories --- shared/inventories.lua | 247 ----- shared/itemcontrol.lua | 2053 +++++++++++++++++++++++++++++++++------ shared/shops.lua | 266 ----- shared/stashcontrol.lua | 707 -------------- starter.lua | 3 - 5 files changed, 1741 insertions(+), 1535 deletions(-) delete mode 100644 shared/inventories.lua delete mode 100644 shared/shops.lua delete mode 100644 shared/stashcontrol.lua diff --git a/shared/inventories.lua b/shared/inventories.lua deleted file mode 100644 index 28e5053..0000000 --- a/shared/inventories.lua +++ /dev/null @@ -1,247 +0,0 @@ -------------------------------------------------------------- --- Item Availability & Inventory Retrieval -------------------------------------------------------------- ---- ---- Locks or unlocks the player's inventory. ---- Freezes/unfreezes the player's position, sets inventory busy state, and toggles hotbar usage. ---- ---- @param toggle boolean True to lock inventory; false to unlock. ---- ---- @usage ---- ```lua ---- lockInv(true) -- Lock inventory. ---- lockInv(false) -- Unlock inventory. ---- ``` -function lockInv(toggle) - FreezeEntityPosition(PlayerPedId(), toggle) - LocalPlayer.state:set("inv_busy", toggle, true) - LocalPlayer.state:set("invBusy", toggle, true) - TriggerEvent('inventory:client:busy:status', toggle) - TriggerEvent('canUseInventoryAndHotbar:toggle', not toggle) -end ---- Checks if a player has the specified items in their inventory. ---- ---- Verifies whether the required quantities are present. Returns a boolean and a table of details. ---- ---- @param items string|table A single item name or table with required amounts. ---- @param amount number The required quantity (default 1). ---- @param src number|nil Player source ID (defaults to caller). ---- @return boolean boolean True if all items are available; otherwise, false. ---- @return table|nil table Table detailing counts for each item. ---- ----@usage ---- ```lua ---- local hasAll, details = hasItem({"health_potion", "mana_potion"}, 2, playerId) ---- if hasAll then ---- -- Proceed with action ---- else ---- -- Inform the player about missing items ---- end ---- ``` -function hasItem(items, amount, src) - local amount = amount and amount or 1 - local grabInv, foundInv = getPlayerInv(src) - if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end - - if grabInv then - local hasTable = {} - for item, amt in pairs(items) do - if not Items[item] then print("^4ERROR^7: ^2Script can't find ingredient item in Shared Items - ^1"..item.."^7") end - - local count = 0 - for _, itemData in pairs(grabInv) do - --jsonPrint(itemData) - if itemData and itemData.name == item then - count += (itemData.amount or itemData.count or 1) - end - end - foundInv = foundInv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6") - local foundMessage = "^6Bridge^7: ^3hasItem^7[^6"..foundInv.."^7]: "..tostring(item).." ^3"..count.."^7/^3"..amt - if count >= amt then foundMessage = foundMessage.." ^5FOUND^7" else foundMessage = foundMessage .." ^1NOT FOUND^7" end - debugPrint(foundMessage) - hasTable[item] = { hasItem = count >= amt, count = count } - end - for k, v in pairs(hasTable) do - if not v.hasItem then - return false, hasTable - end - end - return true, hasTable - end - -- if can't find inventory, return false - print("^1Error^7: ^1Can't find players inventory for some reason") - return false, {} -end - ---- Retrieves a player's inventory based on the active inventory system. ---- ---- @param src number|nil The player source ID (if nil, retrieves current player's inventory). ---- @return table|nil table The inventory items. ---- @return string|nil string The name of the inventory system. ---- ----@usage ---- ```lua ---- local inventory, system = getPlayerInv(playerId) ---- if inventory then ---- -- Process inventory ---- end ---- ``` -function getPlayerInv(src) - local grabInv = nil - local foundInv = "" - - if isStarted(OXInv) then - foundInv = OXInv - if src then - grabInv = exports[OXInv]:GetInventoryItems(src) - else - grabInv = exports[OXInv]:GetPlayerItems() - end - - elseif isStarted(QSInv) then - foundInv = QSInv - if src then - grabInv = exports[QSInv]:GetInventory(src) - else - grabInv = exports[QSInv]:getUserInventory() - end - - elseif isStarted(OrigenInv) then - foundInv = OrigenInv - if src then - grabInv = exports[OrigenInv]:getInventory(src) - else - grabInv = exports[OrigenInv]:GetInventory() - end - - elseif isStarted(CoreInv) then - foundInv = CoreInv - if src then - grabInv = exports[CoreInv]:getInventory(src) - else - grabInv = exports[CoreInv]:getInventory() - end - - elseif isStarted(CodeMInv) then - foundInv = CodeMInv - if src then - grabInv = exports[CodeMInv]:GetInventory(getPlayer(src).citizenId, src) - else - grabInv = exports[CodeMInv]:getUserInventory() - end - - elseif isStarted(TgiannInv) then - foundInv = TgiannInv - if src then - grabInv = exports[TgiannInv]:GetPlayerItems(src) - else - grabInv = exports[TgiannInv]:GetPlayerItems() - end - - elseif isStarted(JPRInv) then - foundInv = JPRInv - if src then - grabInv = Core.Functions.GetPlayer(src).PlayerData.items - else - grabInv = Core.Functions.GetPlayerData().items - end - - elseif isStarted(QBInv) then - foundInv = QBInv - if src then - grabInv = Core.Functions.GetPlayer(src).PlayerData.items - else - grabInv = Core.Functions.GetPlayerData().items - end - - elseif isStarted(PSInv) then - foundInv = PSInv - if src then - grabInv = Core.Functions.GetPlayer(src).PlayerData.items - else - grabInv = Core.Functions.GetPlayerData().items - end - - elseif ESX and isStarted(ESXExport) then - foundInv = ESX - if src then - local xPlayer = ESX.GetPlayerFromId(src) - grabInv = xPlayer.inventory - else - local xPlayer = ESX.GetPlayerData() -- Client side, if available - grabInv = xPlayer.inventory - end - - elseif isStarted(RSGInv) then - foundInv = RSGInv - if src then - grabInv = Core.Functions.GetPlayer(src).PlayerData.items - else - grabInv = Core.Functions.GetPlayerData().items - end - - else - print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3starter^1.^2lua^7") - end - return grabInv, foundInv -end - -function isInventoryOpen() - - return IsNuiFocused() - -end - -------------------------------------------------------------- --- Item Image Retrieval -------------------------------------------------------------- - ---- Retrieves the NUI link for an item's image from the active inventory system. ---- ---- @param item string The item name. ---- @return string string A `nui://` link to the item's image, or an empty string if not found. ---- ---- @usage ---- ```lua ---- local imageLink = invImg("health_potion") ---- if imageLink ~= "" then print(imageLink) end ---- ``` -function invImg(item) - local imgLink = "" - if item ~= "" and Items[item] then - if isStarted(OXInv) then - imgLink = "nui://"..OXInv.."/web/images/"..(Items[item].image or "") - - elseif isStarted(QSInv) then - imgLink = "nui://"..QSInv.."/html/images/"..(Items[item].image or "") - - elseif isStarted(CoreInv) then - imgLink = "nui://"..CoreInv.."/html/img/"..(Items[item].image or "") - - elseif isStarted(CodeMInv) then - imgLink = "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "") - - elseif isStarted(OrigenInv) then - imgLink = "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "") - - elseif isStarted(JPRInv) then - imgLink = "nui://"..JPRInv.."/html/images/"..(Items[item].image or "") - - elseif isStarted(QBInv) then - imgLink = "nui://"..QBInv.."/html/images/"..(Items[item].image or "") - - elseif isStarted(PSInv) then - imgLink = "nui://"..PSInv.."/html/images/"..(Items[item].image or "") - - elseif isStarted(TgiannInv) then - imgLink = "nui://inventory_images/images/"..(Items[item].image or "") - - elseif isStarted(RSGInv) then - imgLink = "nui://"..RSGInv.."/html/images/"..(Items[item].image or "") - - else - print("^4ERROR^7: ^2No Inventory detected for invImg - Check starter.lua") - end - end - return imgLink -end \ No newline at end of file diff --git a/shared/itemcontrol.lua b/shared/itemcontrol.lua index 0b8e751..80d5993 100644 --- a/shared/itemcontrol.lua +++ b/shared/itemcontrol.lua @@ -14,6 +14,1085 @@ ]] validTokens = {} +-- Function Compatability Table +local InvFunc = { + { + invName = OXInv, + removeItem = function(src, item, remamount) + exports[OXInv]:RemoveItem(src, item, remamount, nil) + end, + addItem = function(src, item, amountToAdd, info, slot) + exports[OXInv]:AddItem(src, item, amountToAdd, info, slot) + end, + setItemMetadata = function(data, src) + exports[OXInv]:SetMetadata(src, data.slot, data.metadata) + end, + canCarry = function(itemTable, src) + local resultTable = {} + for k, v in pairs(itemTable) do + resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v) + end + end, + getMaxInvWeight = function() + return exports[OXInv]:GetPlayerMaxWeight() + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = exports[OXInv]:GetInventoryItems(src) + else + grabInv = exports[OXInv]:GetPlayerItems() + end + return grabInv + end, + invImg = function(item) + return "nui://"..OXInv.."/web/images/"..(Items[item].image or "") + end, + openShop = function(data) + exports[OXInv]:openInventory('shop', { type = data.shop }) + end, + registerShop = function(name, label, items, society) + exports[OXInv]:RegisterShop(name, { + name = label, + inventory = items, + society = society, + }) + debugPrint("^6Bridge^7: ^2Registering ^5"..OXInv.." ^3Store^7:", name, "^4Label^7: "..label) + end, + openStash = function(data) + exports[OXInv]:openInventory('stash', data.stash) + end, + clearStash = function(stashId) + exports[OXInv]:ClearInventory(stashId) + end, + getStash = function(stashName) + local stash = exports[OXInv]:Inventory(stashName) + -- Add fallback if ox can't find the stash and returns a boolean + return type(stash) == "table" and stash.items or {} + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + for _, name in pairs(stashName) do + local success = exports[OXInv]:RemoveItem(name, k, v) + if success then + debugPrint("^6Bridge^7: ^2Removing ^3"..OXInv.." ^2Stash item^7:", k, v) + break + end + end + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil) + debugPrint("^6Bridge^7: ^2Registering ^4"..OXInv.." ^3Stash^7:", name, "^4Label^7: "..label) + end, + }, + { + invName = QSInv, + removeItem = function(src, item, remamount) + exports[QSInv]:RemoveItem(src, item, remamount) + end, + addItem = function(src, item, amountToAdd, info, slot) + exports[QSInv]:AddItem(src, item, amountToAdd, slot, info) + end, + setItemMetadata = function(data, src) + exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata) + end, + canCarry = function(itemTable, src) + local resultTable = {} + for k, v in pairs(itemTable) do + resultTable[k] = exports[QSInv]:CanCarryItem(src, k, v) + end + end, + getMaxInvWeight = function() + return InventoryWeight + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = exports[QSInv]:GetInventory(src) + else + grabInv = exports[QSInv]:getUserInventory() + end + return grabInv + end, + invImg = function(item) + return "nui://"..QSInv.."/html/images/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) + end, + registerShop = function(name, label, items, society) + -- + end, + openStash = function(data) + TriggerEvent("inventory:client:SetCurrentStash", data.stash) + TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { + slots = data.slots or 50, + maxWeight = data.maxWeight or 600000 + }) + end, + clearStash = function(stashId) + exports[QSInv]:ClearOtherInventory('stash', stashId) + end, + getStash = function(stashName) + return exports[QSInv]:GetStashItems(stashName) + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + exports[QSInv]:RemoveItemIntoStash(stashName, k, v) + debugPrint("^6Bridge^7: ^2Removing ^3"..QSInv.." ^2Stash item^7:", k, v) + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, + { + invName = CoreInv, + removeItem = function(src, item, remamount) + exports[CoreInv]:removeItem(src, item, remamount) + end, + addItem = function(src, item, amountToAdd, info, slot) + exports[CoreInv]:addItem(src, item, amountToAdd, info) + end, + setItemMetadata = function(data, src) + exports[CoreInv]:setMetadata(src, data.slot, data.metadata) + end, + canCarry = function(itemTable, src) + local resultTable = {} + for k, v in pairs(itemTable) do + resultTable[k] = exports[CoreInv]:canCarry(src, k, v) + end + end, + getMaxInvWeight = function() + return InventoryWeight + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = exports[CoreInv]:getInventory(src) + else + grabInv = exports[CoreInv]:getInventory() + end + return grabInv + end, + invImg = function(item) + return "nui://"..CoreInv.."/html/img/"..(Items[item].image or "") + end, + openShop = function(data) + -- + end, + registerShop = function(name, label, items, society) + -- + end, + openStash = function(data) + TriggerServerEvent('core_inventory:server:openInventory', data.stash, 'stash') + end, + clearStash = function(stashId) + exports[CoreInv]:clearInventory("stash-"..stashId) + end, + getStash = function(stashName) + return exports[CoreInv]:getInventory(stashName) + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + exports[CoreInv]:removeItemExact(stashName, k, v) + debugPrint("^6Bridge^7: ^2Removing ^3"..CoreInv.." ^2Stash item^7:", k, v) + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, + { + invName = OrigenInv, + removeItem = function(src, item, remamount) + exports[OrigenInv]:removeItem(src, item, remamount) + end, + addItem = function(src, item, amountToAdd, info, slot) + exports[OrigenInv]:addItem(src, item, amountToAdd, info, slot) + end, + setItemMetadata = function(data, src) + exports[OrigenInv]:setMetadata(src, data.slot, data.metadata) + end, + canCarry = function(itemTable, src) + local resultTable = {} + for k, v in pairs(itemTable) do + resultTable[k] = exports[OrigenInv]:canCarryItem(src, k, v) + end + return resultTable + end, + getMaxInvWeight = function() + return InventoryWeight + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = exports[OXInv]:GetInventoryItems(src) + else + grabInv = exports[OXInv]:GetPlayerItems() + end + return grabInv + end, + invImg = function(item) + return "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "") + end, + openShop = function(data) + -- + end, + registerShop = function(name, label, items, society) + -- + end, + openStash = function(data) + exports[OrigenInv]:openInventory('stash', data.stash, { label = data.label }) + end, + clearStash = function(stashId) + exports[OrigenInv]:ClearInventory(stashId) + end, + getStash = function(stashName) + return exports[OrigenInv]:getInventory(stashName) + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + exports[OrigenInv]:RemoveFromStash(stashName, k, v) + debugPrint("^6Bridge^7: ^2Removing ^3"..OrigenInv.." ^2Stash item^7:", k, v) + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + exports[OrigenInv]:registerStash(name, label, slots or 50, weight or 4000000) + debugPrint("^6Bridge^7: ^2Registering ^4"..OrigenInv.." ^3Stash^7:", name, "^4Label^7: "..label) + end, + }, + { + invName = CodeMInv, + removeItem = function(src, item, remamount) + exports[CodeMInv]:RemoveItem(src, item, remamount) + end, + addItem = function(src, item, amountToAdd, info, slot) + exports[CodeMInv]:AddItem(src, item, amountToAdd, slot, info) + end, + setItemMetadata = function(data, src) + exports[CodeMInv]:SetItemMetadata(src, data.slot, data.metadata) + end, + canCarry = function(itemTable, src) + local resultTable = {} + local items = getPlayerInv(src) + local totalWeight = 0 + if not items then return false end + for _, item in pairs(items) do + totalWeight += (item.weight * item.amount) + end + for k, v in pairs(itemTable) do + local itemInfo = Items[k] + if not itemInfo then + resultTable[k] = true + else + resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight + end + return resultTable + end + end, + getMaxInvWeight = function() + return InventoryWeight + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = exports[CodeMInv]:GetInventory(getPlayer(src).citizenId, src) + else + grabInv = exports[CodeMInv]:getUserInventory() + end + return grabInv + end, + invImg = function(item) + return "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerEvent("codem-inventory:openshop", data.shop) + end, + registerShop = function(name, label, items, society) + -- + end, + openStash = function(data) + TriggerServerEvent('codem-inventory:server:openstash', data.stash, data.slots, data.maxWeight, data.label) + end, + clearStash = function(stashId) + exports[CodeMInv]:ClearInventory(stashId) + end, + getStash = function(stashName) + return exports[CodeMInv]:GetStashItems(stashName) + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + for l in pairs(stashItems) do + if stashItems[l].name == k then + if (stashItems[l].amount - v) <= 0 then + stashItems[l] = nil + else + debugPrint("^6Bridge^7: ^2Removing ^3"..CodeMInv.." ^2Stash item^7:", k, v) + stashItems[l].amount -= v + end + end + end + end + exports[CodeMInv]:UpdateStash(stashName, stashItems) + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, + { + invName = TgiannInv, + removeItem = function(src, item, remamount) + exports[TgiannInv]:RemoveItem(src, item, remamount) + end, + addItem = function(src, item, amountToAdd, info, slot) + exports[TgiannInv]:AddItem(src, item, amountToAdd, slot, info) + end, + setItemMetadata = function(data, src) + exports[TgiannInv]:UpdateItemMetadata(src, data.item, data.slot, data.metadata) + end, + canCarry = function(itemTable, src) + local resultTable = {} + for k, v in pairs(itemTable) do + resultTable[k] = exports[TgiannInv]:CanCarryItem(src, k, v) + end + return resultTable + end, + getMaxInvWeight = function() + return InventoryWeight + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = exports[TgiannInv]:GetPlayerItems(src) + else + grabInv = exports[TgiannInv]:GetPlayerItems() + end + return grabInv + end, + invImg = function(item) + return "nui://inventory_images/images/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerServerEvent(getScript()..':server:openServerShop', data.shop) + end, + registerShop = function(name, label, items, society) + exports[TgiannInv]:RegisterShop(name, items) + debugPrint("^6Bridge^7: ^2Registering ^5"..TgiannInv.." ^3Store^7:", name, "^4Label^7: "..label) + end, + openStash = function(data) + TriggerServerEvent(getScript()..':server:openServerStash', { + stashName = data.stash, + label = data.label, + maxweight = data.maxWeight or 600000, + slots = data.slots or 40 + }) + end, + clearStash = function(stashId) + exports[TgiannInv]:DeleteInventory("stash", stashId) + end, + getStash = function(stashName) + return exports[TgiannInv]:GetSecondaryInventoryItems("stash", stashName) + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + local itemData = exports[TgiannInv]:GetItemByNameFromSecondaryInventory("stash", stashName, k) + exports[TgiannInv]:RemoveItemFromSecondaryInventory("stash", stashName, k, v, itemData.slot, nil) + debugPrint("^6Bridge^7: ^2Removing ^3"..TgiannInv.." ^2Stash item^7:", k, v) + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + exports[TgiannInv]:RegisterStash(name, label, slots or 50, weight or 4000000) + debugPrint("^6Bridge^7: ^2Registering ^4"..TgiannInv.." ^3Stash^7:", name, "^4Label^7: "..label) + end, + }, + { + invName = JPRInv, + removeItem = function(src, item, remamount) + while remamount > 0 do + if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then + remamount -= 1 + else + print("^1Error removing "..item.." Amount left: "..remamount) + break + end + end + if Config.Crafting ~= nil and Config.Crafting.showItemBox then + TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1) + end + end, + addItem = function(src, item, amountToAdd, info, slot) + if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then + if Config.Crafting ~= nil and Config.Crafting.showItemBox then + TriggerClientEvent("ps-inventory:client:ItemBox", src, Items[item], "add", amountToAdd) + end + end + end, + setItemMetadata = function(data, src) + local Player = Core.Functions.GetPlayer(src) + Player.PlayerData.items[data.slot].info = data.metadata + if data.metadata.durability then + Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability + end + Player.Functions.SetInventory(Player.PlayerData.items) + end, + canCarry = function(itemTable, src) + local resultTable = {} + local items = getPlayerInv(src) + local totalWeight = 0 + if not items then return false end + for _, item in pairs(items) do + totalWeight += (item.weight * item.amount) + end + for k, v in pairs(itemTable) do + local itemInfo = Items[k] + if not itemInfo then + resultTable[k] = true + else + resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight + end + end + return resultTable + end, + getMaxInvWeight = function() + if checkExportExists(JPRInv, "GetMaxWeight") then + return exports[JPRInv]:GetMaxWeight() + else + return InventoryWeight + end + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = Core.Functions.GetPlayer(src).PlayerData.items + else + grabInv = Core.Functions.GetPlayerData().items + end + return grabInv + end, + invImg = function(item) + return "nui://"..JPRInv.."/html/images/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerServerEvent(getScript()..':server:openServerShop', data.shop) + TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) + end, + registerShop = function(name, label, items, society) + -- + end, + openStash = function(data) + if QBInvNew then + TriggerServerEvent(getScript()..':server:openServerStash', { + stashName = data.stash, + label = data.label, + maxweight = data.maxWeight or 600000, + slots = data.slots or 40 + }) + else + TriggerEvent("inventory:client:SetCurrentStash", data.stash) + TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { + slots = data.slots or 50, + maxWeight = data.maxWeight or 600000 + }) + end + end, + clearStash = function(stashId) + MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { + ['stash'] = stashId, + ['items'] = json.encode({}) + }) + end, + getStash = function(stashName) + local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName }) + if result then + return json.decode(result) + else + return {} + end + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + if not stashItems or not next(stashItems) then + stashItems = getStash(stashName) + end + for k, v in pairs(items) do + for l in pairs(stashItems) do + if stashItems[l].name == k then + if (stashItems[l].amount - v) <= 0 then + stashItems[l] = nil + else + debugPrint("^6Bridge^7: ^2Removing ^3"..JPRInv.." ^2Stash item^7:", k, v) + stashItems[l].amount -= v + end + end + end + end + debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3JPR^2 stash '^6"..stashName.."^7'") + MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { + ['stash'] = stashName, + ['items'] = json.encode(stashItems) + }) + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, + { + invName = QBInv, + removeItem = function(src, item, remamount) + if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, remamount, slot) then + TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "remove", amount or 1) + else + print("^1Error removing "..item.." Amount left: "..remamount) + end + end, + addItem = function(src, item, amountToAdd, info, slot) + if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then + TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "add", amountToAdd) + end + end, + setItemMetadata = function(data, src) + local Player = Core.Functions.GetPlayer(src) + Player.PlayerData.items[data.slot].info = data.metadata + if data.metadata.durability then + Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability + end + Player.Functions.SetInventory(Player.PlayerData.items) + end, + canCarry = function(itemTable, src) + local resultTable = {} + if QBInvNew then + for k, v in pairs(itemTable) do + resultTable[k] = exports[QBInv]:CanAddItem(src, k, v) + end + else + local items = getPlayerInv(src) + local totalWeight = 0 + if not items then return false end + for _, item in pairs(items) do + totalWeight += (item.weight * item.amount) + end + for k, v in pairs(itemTable) do + local itemInfo = Items[k] + if not itemInfo then + resultTable[k] = true + else + resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight + end + end + end + return resultTable + end, + getMaxInvWeight = function() + if checkExportExists(QBInv, "GetMaxWeight") then + return exports[QBInv]:GetMaxWeight() + else + return InventoryWeight + end + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = Core.Functions.GetPlayer(src).PlayerData.items + else + grabInv = Core.Functions.GetPlayerData().items + end + return grabInv + end, + invImg = function(item) + return "nui://"..QBInv.."/html/images/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerServerEvent(getScript()..':server:openServerShop', data.shop) + TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) + end, + registerShop = function(name, label, items, society) + if checkExportExists(QBInv, "CreateShop") then + exports[QBInv]:CreateShop({ + name = name, + label = label, + slots = #items, + items = items, + society = society, + }) + debugPrint("^6Bridge^7: ^2Registering ^5"..QBInv.." ^3Store^7:", name, "^4Label^7: "..label) + end + end, + openStash = function(data) + if QBInvNew then + TriggerServerEvent(getScript()..':server:openServerStash', { + stashName = data.stash, + label = data.label, + maxweight = data.maxWeight or 600000, + slots = data.slots or 40 + }) + else + TriggerEvent("inventory:client:SetCurrentStash", data.stash) + TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { + slots = data.slots or 50, + maxWeight = data.maxWeight or 600000 + }) + end + end, + clearStash = function(stashId) + if QBInvNew then + exports[QBInv]:ClearStash(stashId) + else + MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { + ['stash'] = stashId, + ['items'] = json.encode({}) + }) + end + end, + getStash = function(stashName) + if QBInvNew then + local result = exports[QBInv]:GetInventory(stashName) or {} + return result.items or {} + else + local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName }) + if result then + return json.decode(result) + else + return {} + end + end + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + if QBInvNew then + for k, v in pairs(items) do + exports[QBInv]:RemoveItem(stashName, k, v, false, 'crafting') + debugPrint("^6Bridge^7: ^2Removing ^3"..QBInv.." ^2Stash item^7:", k, v) + end + else + if not stashItems or not next(stashItems) then + stashItems = getStash(stashName) + end + for k, v in pairs(items) do + for l in pairs(stashItems) do + if stashItems[l].name == k then + if (stashItems[l].amount - v) <= 0 then + stashItems[l] = nil + else + debugPrint("^6Bridge^7: ^2Removing ^3"..QBInv.." ^2Stash item^7:", k, v) + stashItems[l].amount -= v + end + end + end + end + debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash '^6"..stashName.."^7'") + MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { + ['stash'] = stashName, + ['items'] = json.encode(stashItems) + }) + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, + { + invName = PSInv, + removeItem = function(src, item, remamount) + while remamount > 0 do + if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then + remamount -= 1 + else + print("^1Error removing "..item.." Amount left: "..remamount) + break + end + end + if Config.Crafting ~= nil and Config.Crafting.showItemBox then + TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1) + end + end, + addItem = function(src, item, amountToAdd, info, slot) + if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then + if Config.Crafting ~= nil and Config.Crafting.showItemBox then + TriggerClientEvent("ps-inventory:client:ItemBox", src, Items[item], "add", amountToAdd) + end + end + end, + setItemMetadata = function(data, src) + --debugPrint(src, data.item, 1, data.slot) + local Player = Core.Functions.GetPlayer(src) + Player.PlayerData.items[data.slot].info = data.metadata + if data.metadata.durability then + Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability + end + Player.Functions.SetInventory(Player.PlayerData.items) + end, + canCarry = function(itemTable, src) + local resultTable = {} + local items = getPlayerInv(src) + local totalWeight = 0 + if not items then return false end + for _, item in pairs(items) do + totalWeight += (item.weight * item.amount) + end + for k, v in pairs(itemTable) do + local itemInfo = Items[k] + if not itemInfo then + resultTable[k] = true + else + resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight + end + end + return resultTable + end, + getMaxInvWeight = function() + if checkExportExists(PSInv, "GetMaxWeight") then + return exports[PSInv]:GetMaxWeight() + else + return InventoryWeight + end + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = Core.Functions.GetPlayer(src).PlayerData.items + else + grabInv = Core.Functions.GetPlayerData().items + end + return grabInv + end, + invImg = function(item) + return "nui://"..PSInv.."/html/images/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerServerEvent(getScript()..':server:openServerShop', data.shop) + TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) + end, + registerShop = function(name, label, items, society) + if checkExportExists(PSInv, "CreateShop") then + exports[PSInv]:CreateShop({ + name = name, + label = label, + slots = #items, + items = items, + society = society, + }) + debugPrint("^6Bridge^7: ^2Registering ^5"..PSInv.." ^3Store^7:", name, "^4Label^7: "..label) + end + end, + openStash = function(data) + if QBInvNew then + TriggerServerEvent(getScript()..':server:openServerStash', { + stashName = data.stash, + label = data.label, + maxweight = data.maxWeight or 600000, + slots = data.slots or 40 + }) + else + TriggerEvent("ps-inventory:client:SetCurrentStash", data.stash) + TriggerEvent("inventory:client:SetCurrentStash", data.stash) + TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { + slots = data.slots or 50, + maxWeight = data.maxWeight or 600000 + }) + end + end, + clearStash = function(stashId) + MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { + ['stash'] = stashId, + ['items'] = json.encode({}) + }) + end, + getStash = function(stashName) + local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName }) + if result then + return json.decode(result) + else + return {} + end + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + if not stashItems or not next(stashItems) then + stashItems = getStash(stashName) + end + for k, v in pairs(items) do + for l in pairs(stashItems) do + if stashItems[l].name == k then + if (stashItems[l].amount - v) <= 0 then + stashItems[l] = nil + else + debugPrint("^6Bridge^7: ^2Removing ^3"..PSInv.." ^2Stash item^7:", k, v) + stashItems[l].amount -= v + end + end + end + end + debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3PS^2 stash ^7'^6"..stashName.."^7'") + MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { + ['stash'] = stashName, + ['items'] = json.encode(stashItems) + }) + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, + { + invName = RSGInv, + removeItem = function(src, item, remamount) + while remamount > 0 do + if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then + remamount -= 1 + else + print("^1Error removing "..item.." Amount left: "..remamount) + break + end + end + if Config.Crafting ~= nil and Config.Crafting.showItemBox then + TriggerClientEvent("rsg-inventory:client:ItemBox", src, Items[item], "remove", amount or 1) + end + end, + addItem = function(src, item, amountToAdd, info, slot) + if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then + TriggerClientEvent("rsg-inventory:client:ItemBox", src, Items[item], "add", amountToAdd) + end + end, + setItemMetadata = function(data, src) + local Player = Core.Functions.GetPlayer(src) + Player.PlayerData.items[data.slot].info = data.metadata + if data.metadata.durability then + Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability + end + Player.Functions.SetInventory(Player.PlayerData.items) + end, + canCarry = function(itemTable, src) + local resultTable = {} + for k, v in pairs(itemTable) do + resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v) + end + return resultTable + end, + getMaxInvWeight = function() + if checkExportExists(RSGInv, "GetMaxWeight") then + return exports[RSGInv]:GetMaxWeight() + else + return InventoryWeight + end + end, + getPlayerInv = function(src) + local grabInv = nil + if src then + grabInv = Core.Functions.GetPlayer(src).PlayerData.items + else + grabInv = Core.Functions.GetPlayerData().items + end + return grabInv + end, + invImg = function(item) + return "nui://"..RSGInv.."/html/images/"..(Items[item].image or "") + end, + openShop = function(data) + TriggerServerEvent(getScript()..':server:openServerShop', data.shop) + end, + registerShop = function(name, label, items, society) + exports[RSGInv]:CreateShop({ + name = name, + label = label, + slots = #items, + items = items, + society = society, + }) + debugPrint("^6Bridge^7: ^2Registering ^5"..RSGInv.." ^3Store^7:", name, "^4Label^7: "..label) + end, + openStash = function(data) + TriggerServerEvent(getScript()..':server:openServerStash', { + stashName = data.stash, + label = data.label, + maxweight = data.maxWeight or 600000, + slots = data.slots or 40 + }) + end, + clearStash = function(stashId) + exports[RSGInv]:ClearStash(stashId) + end, + getStash = function(stashName) + return exports[RSGInv]:GetInventory(stashName) + end, + stashAddItem = function(stashItems, stashName, items) + + end, + stashRemoveItem = function(stashItems, stashName, items) + for k, v in pairs(items) do + exports[RSGInv]:RemoveItem(stashName, k, v, false, 'crafting') + debugPrint("^6Bridge^7: ^2Removing ^3"..RSGInv.." ^2Stash item^7:", k, v) + end + end, + registerStash = function(name, label, slots, weight, owner, coords) + -- + end, + }, +} + +------------------------------------------------------------- +-- Item Availability & Inventory Retrieval +------------------------------------------------------------- +--- +--- Locks or unlocks the player's inventory. +--- Freezes/unfreezes the player's position, sets inventory busy state, and toggles hotbar usage. +--- +--- @param toggle boolean True to lock inventory; false to unlock. +--- +--- @usage +--- ```lua +--- lockInv(true) -- Lock inventory. +--- lockInv(false) -- Unlock inventory. +--- ``` +function lockInv(toggle) + FreezeEntityPosition(PlayerPedId(), toggle) + LocalPlayer.state:set("inv_busy", toggle, true) + LocalPlayer.state:set("invBusy", toggle, true) + TriggerEvent('inventory:client:busy:status', toggle) + TriggerEvent('canUseInventoryAndHotbar:toggle', not toggle) +end + +--- Checks if a player has the specified items in their inventory. +--- +--- Verifies whether the required quantities are present. Returns a boolean and a table of details. +--- +--- @param items string|table A single item name or table with required amounts. +--- @param amount number The required quantity (default 1). +--- @param src number|nil Player source ID (defaults to caller). +--- @return boolean boolean True if all items are available; otherwise, false. +--- @return table|nil table Table detailing counts for each item. +--- +---@usage +--- ```lua +--- local hasAll, details = hasItem({"health_potion", "mana_potion"}, 2, playerId) +--- if hasAll then +--- -- Proceed with action +--- else +--- -- Inform the player about missing items +--- end +--- ``` +function hasItem(items, amount, src) + local amount = amount and amount or 1 + local grabInv, foundInv = getPlayerInv(src) + if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end + + if grabInv then + local hasTable = {} + for item, amt in pairs(items) do + if not Items[item] then print("^4ERROR^7: ^2Script can't find ingredient item in Shared Items - ^1"..item.."^7") end + + local count = 0 + for _, itemData in pairs(grabInv) do + --jsonPrint(itemData) + if itemData and itemData.name == item then + count += (itemData.amount or itemData.count or 1) + end + end + foundInv = foundInv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6") + local foundMessage = "^6Bridge^7: ^3hasItem^7[^6"..foundInv.."^7]: "..tostring(item).." ^3"..count.."^7/^3"..amt + if count >= amt then foundMessage = foundMessage.." ^5FOUND^7" else foundMessage = foundMessage .." ^1NOT FOUND^7" end + debugPrint(foundMessage) + hasTable[item] = { hasItem = count >= amt, count = count } + end + for k, v in pairs(hasTable) do + if not v.hasItem then + return false, hasTable + end + end + return true, hasTable + end + -- if can't find inventory, return false + print("^1Error^7: ^1Can't find players inventory for some reason") + return false, {} +end + +--- Retrieves a player's inventory based on the active inventory system. +--- +--- @param src number|nil The player source ID (if nil, retrieves current player's inventory). +--- @return table|nil table The inventory items. +--- @return string|nil string The name of the inventory system. +--- +---@usage +--- ```lua +--- local inventory, system = getPlayerInv(playerId) +--- if inventory then +--- -- Process inventory +--- end +--- ``` +function getPlayerInv(src) + local grabInv = nil + local foundInv = "" + + for _, inv in ipairs(InvFunc) do + if isStarted(inv.invName) then + foundInv = inv.invName + grabInv = inv.getPlayerInv(src) + break + end + end + if foundInv == "" then + print("^4ERROR^7: ^2No Supported Inventory detected ^7- ^2Check ^3starter^1.^2lua^7") + end + return grabInv, foundInv +end + +function isInventoryOpen() + + return IsNuiFocused() + +end + +------------------------------------------------------------- +-- Item Image Retrieval +------------------------------------------------------------- + +--- Retrieves the NUI link for an item's image from the active inventory system. +--- +--- @param item string The item name. +--- @return string string A `nui://` link to the item's image, or an empty string if not found. +--- +--- @usage +--- ```lua +--- local imageLink = invImg("health_potion") +--- if imageLink ~= "" then print(imageLink) end +--- ``` +function invImg(item) + local imgLink = "" + if item ~= "" and doesItemExist(item) then + for _, inv in ipairs(InvFunc) do + if isStarted(inv.invName) then + imgLink = inv.invImg(item) + break + end + end + end + return imgLink +end + ------------------------------------------------------------- -- Registering Usable Items ------------------------------------------------------------- @@ -142,18 +1221,28 @@ end --- ```lua --- TriggerServerEvent(getScript()..":server:toggleItem", true, "health_potion", 1) --- ``` + + RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount, newsrc, info, slot, token) --debugPrint(GetInvokingResource()) if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7") return end + if not Items[item] then print("^6Bridge^7: ^1Error^7 - ^2Tried to "..(tostring(give) == "true" and "add" or "remove").." '^3"..item.."^7' but it doesn't exist") return end - local src = newsrc or source + local src = (newsrc and tonumber(newsrc)) or source + + -- Only server resources may set newsrc; clients cannot. + if newsrc ~= nil and source ~= 0 then + debugPrint("^1DENY^7: client tried to set newsrc") + return + end + if (give == true or give == 1) then if newsrc == nil then -- this must be coming from client this would be blank if not checkToken(src, token, "item", item) then @@ -166,102 +1255,25 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount, local remamount = amount or 1 if item == nil then return end - -- Grab the current inventory (you can expand usage of 'inv' if needed) local invName = "" if give == 0 or give == false then if not hasItem(item, amount or 1, src) then dupeWarn(src, item, amount) else - if isStarted(OXInv) then - invName = OXInv - exports[OXInv]:RemoveItem(src, item, remamount, nil) - - elseif isStarted(QSInv) then - invName = QSInv - exports[QSInv]:RemoveItem(src, item, remamount) - - elseif isStarted(CoreInv) then - invName = CoreInv - exports[CoreInv]:removeItem(src, item, remamount) - - elseif isStarted(OrigenInv) then - invName = OrigenInv - exports[OrigenInv]:removeItem(src, item, remamount) - - elseif isStarted(CodeMInv) then - invName = CodeMInv - exports[CodeMInv]:RemoveItem(src, item, remamount) - - elseif isStarted(TgiannInv) then - invName = TgiannInv - exports[TgiannInv]:RemoveItem(src, item, remamount) - - elseif isStarted(JPRInv) then - invName = JPRInv - while remamount > 0 do - if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then - remamount -= 1 - else - print("^1Error removing "..item.." Amount left: "..remamount) - break - end + for _, inv in ipairs(InvFunc) do + if isStarted(inv.invName) then + invName = inv.invName + inv.removeItem(src, item, remamount) + break end - if Config.Crafting ~= nil and Config.Crafting.showItemBox then - TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1) - end - - elseif isStarted(QBInv) then - invName = QBInv - if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, remamount, slot) then - TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "remove", amount or 1) - else - print("^1Error removing "..item.." Amount left: "..remamount) - end - --while remamount > 0 do - -- if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then - -- remamount -= 1 - -- else - -- print("^1Error removing "..item.." Amount left: "..remamount) - -- break - -- end - --end - --if Config.Crafting ~= nil and Config.Crafting.showItemBox then - -- TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "remove", amount or 1) - --end - - elseif isStarted(PSInv) then - invName = PSInv - while remamount > 0 do - if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then - remamount -= 1 - else - print("^1Error removing "..item.." Amount left: "..remamount) - break - end - end - if Config.Crafting ~= nil and Config.Crafting.showItemBox then - TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1) - end - - elseif isStarted(RSGInv) then invName = RSGInv - while remamount > 0 do - if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then - remamount -= 1 - else - print("^1Error removing "..item.." Amount left: "..remamount) - break - end - end - if Config.Crafting ~= nil and Config.Crafting.showItemBox then - TriggerClientEvent("rsg-inventory:client:ItemBox", src, Items[item], "remove", amount or 1) - end - end + ----- -- Fallback for if no inventory found: ----- if invName == "" then + print("^4ERROR^7: No Supported Inventory detected - ^2Falling back to core functions") if isStarted(QBExport) or isStarted(QBXExport) then -- if qbcore or qbxcore, just use core functions invName = isStarted(QBXExport) and QBXExport or isStarted(QBExport) and QBExport Core.Functions.GetPlayer(src).Functions.RemoveItem(item, remamount, slot) @@ -271,61 +1283,22 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount, ESX.GetPlayerFromId(src).removeInventoryItem(item, remamount) end - end - -- Final check for if inventory was found - if invName == "" then - print("^4ERROR^7: No Inventory detected - Check starter.lua") else debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1)) end end else local amountToAdd = amount or 1 - if isStarted(OXInv) then invName = OXInv - exports[OXInv]:AddItem(src, item, amountToAdd, info, slot) - - elseif isStarted(QSInv) then invName = QSInv - exports[QSInv]:AddItem(src, item, amountToAdd, slot, info) - - elseif isStarted(CoreInv) then invName = CoreInv - exports[CoreInv]:addItem(src, item, amountToAdd, info) - - elseif isStarted(CodeMInv) then invName = CodeMInv - exports[CodeMInv]:AddItem(src, item, amountToAdd, slot, info) - - elseif isStarted(OrigenInv) then invName = OrigenInv - exports[OrigenInv]:addItem(src, item, amountToAdd, info, slot) - - elseif isStarted(TgiannInv) then invName = TgiannInv - exports[TgiannInv]:AddItem(src, item, amountToAdd, slot, info) - - elseif isStarted(JPRInv) then invName = JPRInv - if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then - if Config.Crafting ~= nil and Config.Crafting.showItemBox then - TriggerClientEvent("ps-inventory:client:ItemBox", src, Items[item], "add", amountToAdd) - end + for _, inv in ipairs(InvFunc) do + if isStarted(inv.invName) then + invName = inv.invName + inv.addItem(src, item, amountToAdd, info, slot) + break end - - elseif isStarted(QBInv) then invName = QBInv - if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then - TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "add", amountToAdd) - end - - elseif isStarted(PSInv) then invName = PSInv - if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then - if Config.Crafting ~= nil and Config.Crafting.showItemBox then - TriggerClientEvent("ps-inventory:client:ItemBox", src, Items[item], "add", amountToAdd) - end - end - - elseif isStarted(RSGInv) then invName = RSGInv - if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then - TriggerClientEvent("rsg-inventory:client:ItemBox", src, Items[item], "add", amountToAdd) - end - end if invName == "" then + print("^4ERROR^7: No Supported Inventory detected - ^2Falling back to core functions") if isStarted(QBExport) or isStarted(QBXExport) then -- if qbcore or qbxcore, just use core functions invName = isStarted(QBXExport) and QBXExport or isStarted(QBExport) and QBExport Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) @@ -334,11 +1307,6 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount, invName = ESX ESX.GetPlayerFromId(src).addInventoryItem(item, amountToAdd) end - end - - -- Final check for if inventory was found - if invName == "" then - print("^4ERROR^7: No Inventory detected - Check starter.lua") else debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1)) end @@ -410,6 +1378,7 @@ end --- ```lua --- useToolDegrade({ item = "drill", maxUse = 10 }) --- ``` + function useToolDegrade(data) -- WIP local metadata, slot = getItemMetadata(data.item) metadata = metadata or {} @@ -424,6 +1393,7 @@ function useToolDegrade(data) -- WIP end end +-- Grab whole inventory and check for metadata function getItemMetadata(item, slot, src) local lowestSlot = 100 local chosenSlot = slot @@ -467,32 +1437,11 @@ end --- ``` RegisterNetEvent(getScript()..":server:setItemMetaData", function(data, src) local src = src or source - if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then - --debugPrint(src, data.item, 1, data.slot) - local Player = Core.Functions.GetPlayer(src) - Player.PlayerData.items[data.slot].info = data.metadata - if data.metadata.durability then - Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + inv.setItemMetadata(data, src) + break end - Player.Functions.SetInventory(Player.PlayerData.items) - - elseif isStarted(OXInv) then - exports[OXInv]:SetMetadata(src, data.slot, data.metadata) - - elseif isStarted(QSInv) then - exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata) - - elseif isStarted(CoreInv) then - exports[CoreInv]:setMetadata(src, data.slot, data.metadata) - - elseif isStarted(CodeMInv) then - exports[CodeMInv]:SetItemMetadata(src, data.slot, data.metadata) - - elseif isStarted(OrigenInv) then - exports[OrigenInv]:setMetadata(src, data.slot, data.metadata) - - elseif isStarted(TgiannInv) then - exports[TgiannInv]:UpdateItemMetadata(src, data.item, data.slot, data.metadata) end end) @@ -565,101 +1514,10 @@ end --- end function canCarry(itemTable, src) local resultTable = {} - if src then - if isStarted(OXInv) then - for k, v in pairs(itemTable) do - resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v) - end - - elseif isStarted(QSInv) then - for k, v in pairs(itemTable) do - resultTable[k] = exports[QSInv]:CanCarryItem(src, k, v) - end - - elseif isStarted(CoreInv) then - for k, v in pairs(itemTable) do - resultTable[k] = exports[CoreInv]:canCarry(src, k, v) - end - - elseif isStarted(CodeMInv) then --- This really needs updating, their docs are confusing.. - local items = getPlayerInv(src) - local totalWeight = 0 - if not items then return false end - for _, item in pairs(items) do - totalWeight += (item.weight * item.amount) - end - for k, v in pairs(itemTable) do - local itemInfo = Items[k] - if not itemInfo then - resultTable[k] = true - else - resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight - end - end - - elseif isStarted(OrigenInv) then - for k, v in pairs(itemTable) do - resultTable[k] = exports[OrigenInv]:canCarryItem(src, k, v) - end - - elseif isStarted(TgiannInv) then - for k, v in pairs(itemTable) do - resultTable[k] = exports[TgiannInv]:CanCarryItem(src, k, v) - end - - elseif isStarted(JPRInv) then - local items = getPlayerInv(src) - local totalWeight = 0 - if not items then return false end - for _, item in pairs(items) do - totalWeight += (item.weight * item.amount) - end - for k, v in pairs(itemTable) do - local itemInfo = Items[k] - if not itemInfo then - resultTable[k] = true - else - resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight - end - end - - elseif isStarted(QBInv) then - if QBInvNew then - for k, v in pairs(itemTable) do - resultTable[k] = exports[QBInv]:CanAddItem(src, k, v) - end - else - local items = getPlayerInv(src) - local totalWeight = 0 - if not items then return false end - for _, item in pairs(items) do - totalWeight += (item.weight * item.amount) - end - for k, v in pairs(itemTable) do - local itemInfo = Items[k] - if not itemInfo then - resultTable[k] = true - else - resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight - end - end - end - - elseif isStarted(PSInv) then - local items = getPlayerInv(src) - local totalWeight = 0 - if not items then return false end - for _, item in pairs(items) do - totalWeight += (item.weight * item.amount) - end - for k, v in pairs(itemTable) do - local itemInfo = Items[k] - if not itemInfo then - resultTable[k] = true - else - resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight - end - end + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + resultTable = inv.canCarry(itemTable, src) + break end end return resultTable @@ -697,65 +1555,13 @@ end function getMaxInvWeight() local weight = 0 - if isStarted(QBInv) then - if checkExportExists(QBInv, "GetMaxWeight") then - return exports[QBInv]:GetMaxWeight() - else - return InventoryWeight + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + weight = inv.getMaxInvWeight() + break end end - if isStarted(PSInv) then - if checkExportExists(PSInv, "GetMaxWeight") then - return exports[PSInv]:GetMaxWeight() - else - return InventoryWeight - end - end - if isStarted(RSGInv) then - if checkExportExists(RSGInv, "GetMaxWeight") then - return exports[RSGInv]:GetMaxWeight() - else - return InventoryWeight - end - end - if isStarted(JPRInv) then - if checkExportExists(JPRInv, "GetMaxWeight") then - return exports[JPRInv]:GetMaxWeight() - else - return InventoryWeight - end - end - - if isStarted(OXInv) then - return exports[OXInv]:GetPlayerMaxWeight() - end - - if isStarted(QSInv) then - return InventoryWeight - end - - if isStarted(CoreInv) then - return InventoryWeight - end - - if isStarted(CodeMInv) then - return InventoryWeight - end - - if isStarted(OrigenInv) then - return InventoryWeight - end - - if isStarted(TgiannInv) then - return InventoryWeight - end - - -- For ESX default inventory (es_extended) - if ESX and isStarted(ESXExport) then - return InventoryWeight - end - - return InventoryWeight + return weight end function getCurrentInvWeight() @@ -865,4 +1671,627 @@ function getItemLabel(item) return Items[item].label end return item.." (Missing)" +end + + +--[[ + Stash Management Module + ------------------------- + This module handles stash-related operations including: + • Retrieving stash items (from server or local cache). + • Checking for required items in stashes. + • Opening stashes using different inventory systems. + • Removing items from stashes. + • Checking if a stash has specific items. +]] + +-- Global variable to hold the current stash (used in callbacks). +local stash + +-- If running on the server, create a callback to retrieve stash items. +if isServer() then + createCallback(getScript()..':server:GetStashItems', function(source, stashName) + if stashName == nil or stashName == "" then + return {} + end + stash = getStash(stashName) + return stash + end) +end + +-- Local cache for stashes. +local stashCache = {} + +--- Retrieves (or updates) a local stash cache entry with a timeout. +--- When the cache is empty or expired, it triggers a server callback to update the items. +--- +--- @param stashName string The name of the stash. +--- @param stop boolean (Optional) If true, clears the entire stash cache. +--- @return boolean True if items exist in cache (and recheck is skipped), false otherwise. +--- +--- @usage +--- ```lua +--- local cached = GetStashTimeout("playerStash") +--- ``` +function GetStashTimeout(stashName, stop) + if stop or (stashName == nil or stashName == "") then + stashCache = {} + return false + end + + -- Retrieve cache for this stash, or initialize if not present. + stash = stashCache[stashName] + if not stash then + debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache ^1not ^2found^7, ^2need to grab from server^7") + stashCache[stashName] = { items = {}, timeout = 0 } + stash = stashCache[stashName] + else + debugPrint("^6Bridge^7: ^2Local Stash for ^7'^3"..stashName.."^7'^2 cache found^7") + end + + -- If there are already items in cache, skip recheck. + if countTable(stashCache[stashName].items) > 0 then + debugPrint("^6Bridge^7: '^3"..stashName.."^7' ^2Items found in local cache, skipping server recheck") + return true + end + + -- If timeout has expired, update the stash items from the server. + if stashCache[stashName].timeout <= 0 then + stashCache[stashName].items = triggerCallback(getScript()..':server:GetStashItems', stashName) + stashCache[stashName].timeout = 10000 -- Timeout in milliseconds. + CreateThread(function() + while stashCache[stashName] and stashCache[stashName].timeout > 0 do + stashCache[stashName].timeout -= 1000 + Wait(1000) + end + debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache timed out^7, ^3Clearing^7") + stashCache[stashName] = nil + end) + end + return false +end + +--- Checks if the specified stashes have the required items. +--- +--- If multiple stashes are provided (as a table), it iterates over each until all required items are found. +--- +--- @param stashes string|table Either a single stash name or a table of stash names. +--- @param itemTable table A table where keys are item names and values are the required amounts. +--- @return boolean, string|nil `boolean, string` true and the stash name if found, otherwise false and nil. +--- +--- @usage +--- ```lua +--- local found, stashName = checkStashItem({"playerStash", "storageStash"}, { iron = 2, wood = 5 }) +--- ``` +function checkStashItem(stashes, itemTable) + if not stashes or stashes == "" then + return hasItem(itemTable), nil + end + + if type(stashes) == "table" then + debugPrint("^6Bridge^7: ^2Checking multiple stashes for ingredients^7") + -- Iterate over each provided stash name. + for _, name in pairs(stashes) do + GetStashTimeout(name) + if stashhasItem(stashCache[name].items, itemTable, nil) then + return true, name + end + end + else + debugPrint("^6Bridge^7: ^2Checking "..(stashes and " '^3"..stashes.."^7'" or "").." ingredients") + GetStashTimeout(stashes) + return stashhasItem(stashCache[stashes].items, itemTable), stashes + end + + return false, nil +end + +------------------------------------------------------------- +-- Stash Opening Functions +------------------------------------------------------------- + +--- Opens a stash using the active inventory system. +--- +--- Checks for job or gang restrictions before opening the stash. +--- +--- @param data table A table containing stash data: +--- - stash (string): The stash identifier. +--- - label (string): Display label. +--- - maxWeight (number|nil): Maximum weight (default 600000). +--- - slots (number|nil): Number of slots (default 40). +--- - stashOptions (table|nil): Additional options for the stash. +--- - job/gang (string|nil): Restriction for access. +--- - coords (vector3): Coordinates to "look" at. +--- +--- @usage +--- ```lua +--- openStash({ stash = "playerStash", label = "Player Stash", coords = vector3(100, 200, 30) }) +--- ``` +function openStash(data) + if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end + + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + inv.openStash(data) + lookEnt(data.coords) + return + end + end + + --Fallback to these commands + TriggerEvent("inventory:client:SetCurrentStash", data.stash) + TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { + slots = data.slots or 50, + maxWeight = data.maxWeight or 600000 + }) + + lookEnt(data.coords) +end + + +-- Wrapper function for opening stash from the server. +-- Messy but not much else I can do about it. +RegisterNetEvent(getScript()..":server:openServerStash", function(data) + local src = source + if isStarted(TgiannInv) then + exports[TgiannInv]:OpenInventory(source, 'stash', data.stashName, data) + end + if isStarted(JPRInv) then + exports[JPRInv]:OpenInventory(source, data.stashName, data) + end + if isStarted(QBInv) then + exports[QBInv]:OpenInventory(source, data.stashName, data) + end + if isStarted(PSInv) then + exports[PSInv]:OpenInventory(source, data.stashName, data) + end + if isStarted(RSGInv) then + exports[RSGInv]:OpenInventory(source, data.stashName, data) + end +end) + +function clearStash(stashId) + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + debugPrint("^5Bridge^7: ^2Clearing ^3"..inv.invName.."^2 Stash^7:", stashId) + inv.clearStash(stashId) + return + end + end +end + + +------------------------------------------------------------- +-- Stash Retrieval Function +------------------------------------------------------------- + +--- Retrieves stash items from the active inventory system. +--- +--- This function converts the raw stash items into a standardized table using the global Items lookup. +--- +--- @param stashName string The identifier for the stash. +--- @return stashTable table A table of items from the stash. +--- +--- @usage +--- ```lua +--- local items = getStash("playerStash") +--- ``` +function getStash(stashName) + local stashResource = "" + if stashName == "" or type(stashName) ~= "string" then + print("^6Bridge^7: ^2Stash name was not a string ^3"..stashName.."^7(^3"..type(stashName).."^7)") + return {} + end + + local stashItems, items = {}, {} + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + debugPrint("^6Bridge^7: ^2Retrieving ^3"..inv.invName.." ^2Stash^7:", stashName) + stashItems = inv.getStash(stashName) + break + end + end + + if stashItems then + for _, item in pairs(stashItems) do + local itemInfo = Items[item.name:lower()] + if itemInfo then + local indexNum = #items + 1 -- Fallback index if slot is missing. + items[(item.slot or indexNum)] = { + name = itemInfo.name or nil, + amount = tonumber(item.amount) or tonumber(item.count), + info = item.info or "", + label = itemInfo.label or nil, + description = itemInfo.description or "", + weight = itemInfo.weight or nil, + type = itemInfo.type or nil, + unique = itemInfo.unique or nil, + useable = itemInfo.useable or nil, + image = itemInfo.image or nil, + slot = (item.slot and item.slot) or indexNum, + metadata = (item.metadata and item.metadata) or nil, + } + end + end + debugPrint("^6Bridge^7: ^3GetStashItems^7: ^2Stash information for '^6"..stashName.."^7' retrieved") + end + jsonPrint(items) + return items +end + +------------------------------------------------------------- +-- Stash Item Removal Function +------------------------------------------------------------- + +--- Removes items from a stash using the active inventory system. +--- +--- Iterates over the provided items and adjusts the stash contents accordingly. +--- +--- @param stashItems table The current stash items. +--- @param stashName string|table The stash identifier (or table of identifiers). +--- @param items table A table of items to remove (keys are item names, values are amounts). +--- +--- @usage +--- ```lua +--- stashRemoveItem(currentItems, "playerStash", { iron = 2, wood = 5 }) +--- ``` +function stashRemoveItem(stashItems, stashName, items) + if stashName == "" or stashName == nil then + print("^1ERROR^7: ^1stashRemoveItem triggered but stashName was empty^7") + return + end + if type(stashName) ~= "table" then + stashName = { stashName } + end + + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + inv.stashRemoveItem(stashItems, stashName[1], items) + return + end + end + + print("^4ERROR^7: ^2No supported Inventory detected ^7- ^2Check ^3starter^1.^2lua^7") +end +RegisterNetEvent(getScript()..":server:stashRemoveItem", stashRemoveItem) + +------------------------------------------------------------- +-- Stash Item Availability Check +------------------------------------------------------------- + +--- Checks whether a stash has the required amount of specific items. +--- +--- It iterates through the provided items and tallies available quantities. +--- +--- @param stashItems table The items available in the stash. +--- @param items string|table The item name or table of required items (key: item, value: amount). +--- @param amount number (Optional) The required amount (if a single item is provided). +--- @return boolean, table (`boolean, string`) Returns true (and a table with counts) if all items are available; false otherwise. +--- +--- @usage +--- ```lua +--- local hasAll, details = stashhasItem(currentStashItems, { iron = 2, wood = 5 }) +--- ``` +function stashhasItem(stashItems, items, amount) + local invs = { OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv, JPRInv, QBInv, PSInv, RSGInv } + local foundInv = "" + for _, inv in ipairs(invs) do + if isStarted(inv) then + foundInv = inv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6") + break + end + end + + -- Ensure items is a table. + if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end + + local hasTable = {} + for item, requiredAmount in pairs(items) do + local count = 0 + for _, itemData in pairs(stashItems) do + if itemData and (itemData.name == item) then + count += (itemData.amount or 1) + end + end + + local debugMsg = string.format("^6Bridge^7: ^3stashHasItem^7[^6%s^7]: %s '%s' ^3%d^7/^3%d^7", foundInv, (count >= requiredAmount and "^5FOUND^7" or "^1NOT FOUND^7"), item, count, requiredAmount) + debugPrint(debugMsg) + + hasTable[item] = { hasItem = (count >= requiredAmount), count = count } + end + + for k, v in pairs(hasTable) do if v.hasItem == false then return false, hasTable end end + + return true, hasTable +end + + +--- Registers a stash with the active inventory system. +--- Supports either OXInv or QSInv. +--- +--- @param name string Unique stash identifier. +--- @param label string Display name for the stash. +--- @param slots number|nil (Optional) Number of slots (default 50). +--- @param weight number|nil (Optional) Maximum weight (default 4000000). +--- @param owner string|nil (Optional) Owner identifier for personal stashes. +--- @param coords table|nil (Optional) Coordinates for the stash location. +--- @usage +--- ```lua +--- registerStash( +--- "playerStash", +--- "Player Stash", +--- 100, +--- 8000000, +--- "player123", +--- { x = 100.0, y = 200.0, z = 30.0 } +--- ) +--- ``` +function registerStash(name, label, slots, weight, owner, coords) + for _, inv in pairs(InvFunc) do + if isStarted(inv.invName) then + inv.registerStash(name, label, slots, weight, owner, coords) + return + end + end +end + +if isServer() then + --- Registers an event to create an OX stash from the server. + --- When triggered, it calls registerStash with the provided parameters. + --- + --- @event server:makeOXStash + --- @param name string Unique stash identifier. + --- @param label string Display name for the stash. + --- @param slots number|nil (Optional) Number of slots. + --- @param weight number|nil (Optional) Maximum weight. + --- @param owner string|nil (Optional) Owner identifier. + --- @param coords table|nil (Optional) Stash coordinates. + --- @usage + --- ```lua + --- TriggerEvent(getScript()..":server:makeOXStash", name, label, slots, weight, owner, coords, token) + --- ``` + RegisterNetEvent(getScript()..":server:makeOXStash", function(name, label, slots, weight, owner, coords, token) + local src = source or nil + if src then + if not checkToken(src, token, "stash", name) then + return + end + end + + registerStash(name, label, slots, weight, owner, coords) + end) +end + +RegisterNetEvent(getScript()..":openGrabBox", function(data) + if isStarted(OXInv) then + return + end + local id = "" + if data.metadata then + id = data.metadata.id + elseif data.info then + id = data.info.id + end + openStash({ + stash = id, + }) +end) + + +------------------------------------------------------------- +-- Selling Menu and Animation +------------------------------------------------------------- + +--- Opens a selling menu with available items and prices. +--- +--- @param data table Contains selling menu data: +--- - sellTable (`table`) Table with Header and Items (item names and prices). +--- - ped (optional) (`number`) Ped entity involved. +--- - onBack (optional) (`function`) Callback for returning. +--- @usage +--- ```lua +--- sellMenu({ +--- sellTable = { +--- Header = "Sell Items", +--- Items = { +--- ["gold_ring"] = 100, +--- ["diamond"] = 500, +--- }, +--- }, +--- ped = pedEntity, +--- onBack = function() print("Returning to previous menu") end, +--- }) +--- ``` +function sellMenu(data) + local origData = data + local Menu = {} + if data.sellTable.Items then + local itemList = {} + for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end + local _, hasTable = hasItem(itemList) + for k, v in pairsByKeys(data.sellTable.Items) do + if Items[k] then + Menu[#Menu + 1] = { + isMenuHeader = not hasTable[k].hasItem, + icon = invImg(k), + header = Items[k].label..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""), + txt = (Loc and Loc[Config.Lan]) and Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"] + or "Sell ALL at $"..v.." each", + onSelect = function() + sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end }) + end, + } + end + end + else + for k, v in pairsByKeys(data.sellTable) do + if type(v) == "table" then + Menu[#Menu + 1] = { + arrow = true, + header = k, + txt = "Amount of items: "..countTable(v.Items), + onSelect = function() + v.onBack = function() + sellMenu(origData) + end + v.sellTable = data.sellTable[k] + sellMenu(v) + end, + } + end + end + end + openMenu(Menu, { + header = data.sellTable.Header or "Amount of items: "..countTable(data.sellTable.Items or data.sellTable), + headertxt = data.sellTable.Header and "Amount of items: "..countTable(data.sellTable.Items or data.sellTable), + canClose = true, + onBack = data.onBack, + }) +end + +--- Plays the selling animation and processes the sale transaction. +--- +--- Checks if the player has the item, plays animations, triggers the server event for selling, +--- and then calls the onBack callback if provided. +--- +--- @param data table Contains: +--- `- item: The item to sell. +--- `- price: Price per item. +--- `- ped (optional): Ped entity involved. +--- `- onBack (optional): Callback to call on completion. +---@usage +--- ```lua +--- sellAnim({ +--- item = "gold_ring", +--- price = 100, +--- ped = pedEntity, +--- onBack = function() sellMenu(data) end, +--- }) +--- ``` +function sellAnim(data) + if not hasItem(data.item, 1) then + triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error") + return + end + + -- Remove any attached clipboard objects. + for _, obj in pairs(GetGamePool('CObject')) do + for _, model in pairs({ `p_cs_clipboard` }) do + if GetEntityModel(obj) == model and IsEntityAttachedToEntity(data.ped, obj) then + DeleteObject(obj) + DetachEntity(obj, 0, 0) + SetEntityAsMissionEntity(obj, true, true) + Wait(100) + DeleteEntity(obj) + end + end + end + + TriggerServerEvent(getScript().."Sellitems", data) + lookEnt(data.ped) + local dict = "mp_common" + playAnim(dict, "givetake2_a", 0.3, 48) + playAnim(dict, "givetake2_b", 0.3, 48, data.ped) + Wait(2000) + StopAnimTask(PlayerPedId(), dict, "givetake2_a", 0.5) + StopAnimTask(data.ped, dict, "givetake2_b", 0.5) + if data.onBack then + data.onBack() + end +end + +--- Server event handler for processing item sales. +--- Removes sold items from inventory and funds the player based on the sale. +RegisterNetEvent(getScript().."Sellitems", function(data) + local src = source + local hasItems, hasTable = hasItem(data.item, 1, src) + if hasItems then + removeItem(data.item, hasTable[data.item].count, src) + fundPlayer((hasTable[data.item].count * data.price), "cash", src) + else + triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error", src) + end +end) + +------------------------------------------------------------- +-- Shop Interface +------------------------------------------------------------- + +--- Opens a shop interface for the player. +--- +--- Checks job/gang restrictions, then uses the active inventory system to open the shop. +--- @param data table Contains: +--- - shop (`string`) The shop identifier. +--- - items (`table`) The items available in the shop. +--- - coords (`vector3`) where the shop is located. +--- - job/gang (optional) (`string`) Job or gang requirements. +---@usage +--- ```lua +--- openShop({ +--- shop = "weapon_shop", +--- items = weaponShopItems, +--- coords = vector3(100.0, 200.0, 300.0), +--- job = "police", +--- }) +--- ``` +function openShop(data) + jsonPrint(data) + if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end + + if Config.General.JimShops then + TriggerServerEvent("jim-shops:ShopOpen", "shop", data.items.label, data.items) + lookEnt(data.coords) + end + + for _, inv in ipairs(InvFunc) do + if isStarted(inv.invName) then + inv.openShop(data) + lookEnt(data.coords) + break + end + end +end + +RegisterNetEvent(getScript()..':server:openServerShop', function(data) + if isStarted(QBInv) and checkExportExists(QBInv, "OpenShop") then + exports[QBInv]:OpenShop(source, data) + end + if isStarted(PSInv) and checkExportExists(PSInv, "OpenShop") then + exports[PSInv]:OpenShop(source, data) + end + if isStarted(TgiannInv) then + exports[TgiannInv]:OpenShop(source, data) + end + if isStarted(RSGInv) then + exports[RSGInv]:OpenShop(source, data) + end +end) + +--- Registers a shop with the active inventory system. +--- Supports either OXInv or QBInv (with QBInvNew flag). +--- +--- @param name string Unique shop identifier. +--- @param label string Display name for the shop. +--- @param items table List of available shop items. +--- @param society string|nil (Optional) Society identifier for shared shops. +--- @usage +--- ```lua +--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons") +--- ``` +function registerShop(name, label, items, society) + local shopResource = "" + + for _, inv in ipairs(InvFunc) do + if isStarted(inv.invName) then + shopResource = inv.invName + inv.registerShop(name, label, items, society) + break + end + end + + if shopResource ~= "" then + debugPrint("^6Bridge^7: ^2Registering ^5"..shopResource.." ^3Store^7:", name, "^4Label^7: "..label) + else + -- debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register shop^7:", name) + end end \ No newline at end of file diff --git a/shared/shops.lua b/shared/shops.lua deleted file mode 100644 index 328e839..0000000 --- a/shared/shops.lua +++ /dev/null @@ -1,266 +0,0 @@ -------------------------------------------------------------- --- Selling Menu and Animation -------------------------------------------------------------- - ---- Opens a selling menu with available items and prices. ---- ---- @param data table Contains selling menu data: ---- - sellTable (`table`) Table with Header and Items (item names and prices). ---- - ped (optional) (`number`) Ped entity involved. ---- - onBack (optional) (`function`) Callback for returning. ---- @usage ---- ```lua ---- sellMenu({ ---- sellTable = { ---- Header = "Sell Items", ---- Items = { ---- ["gold_ring"] = 100, ---- ["diamond"] = 500, ---- }, ---- }, ---- ped = pedEntity, ---- onBack = function() print("Returning to previous menu") end, ---- }) ---- ``` -function sellMenu(data) - local origData = data - local Menu = {} - if data.sellTable.Items then - local itemList = {} - for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end - local _, hasTable = hasItem(itemList) - for k, v in pairsByKeys(data.sellTable.Items) do - if Items[k] then - Menu[#Menu + 1] = { - isMenuHeader = not hasTable[k].hasItem, - icon = invImg(k), - header = Items[k].label..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""), - txt = (Loc and Loc[Config.Lan]) and Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"] - or "Sell ALL at $"..v.." each", - onSelect = function() - sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end }) - end, - } - end - end - else - for k, v in pairsByKeys(data.sellTable) do - if type(v) == "table" then - Menu[#Menu + 1] = { - arrow = true, - header = k, - txt = "Amount of items: "..countTable(v.Items), - onSelect = function() - v.onBack = function() - sellMenu(origData) - end - v.sellTable = data.sellTable[k] - sellMenu(v) - end, - } - end - end - end - openMenu(Menu, { - header = data.sellTable.Header or "Amount of items: "..countTable(data.sellTable.Items or data.sellTable), - headertxt = data.sellTable.Header and "Amount of items: "..countTable(data.sellTable.Items or data.sellTable), - canClose = true, - onBack = data.onBack, - }) -end - ---- Plays the selling animation and processes the sale transaction. ---- ---- Checks if the player has the item, plays animations, triggers the server event for selling, ---- and then calls the onBack callback if provided. ---- ---- @param data table Contains: ---- `- item: The item to sell. ---- `- price: Price per item. ---- `- ped (optional): Ped entity involved. ---- `- onBack (optional): Callback to call on completion. ----@usage ---- ```lua ---- sellAnim({ ---- item = "gold_ring", ---- price = 100, ---- ped = pedEntity, ---- onBack = function() sellMenu(data) end, ---- }) ---- ``` -function sellAnim(data) - if not hasItem(data.item, 1) then - triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error") - return - end - - -- Remove any attached clipboard objects. - for _, obj in pairs(GetGamePool('CObject')) do - for _, model in pairs({ `p_cs_clipboard` }) do - if GetEntityModel(obj) == model and IsEntityAttachedToEntity(data.ped, obj) then - DeleteObject(obj) - DetachEntity(obj, 0, 0) - SetEntityAsMissionEntity(obj, true, true) - Wait(100) - DeleteEntity(obj) - end - end - end - - TriggerServerEvent(getScript().."Sellitems", data) - lookEnt(data.ped) - local dict = "mp_common" - playAnim(dict, "givetake2_a", 0.3, 48) - playAnim(dict, "givetake2_b", 0.3, 48, data.ped) - Wait(2000) - StopAnimTask(PlayerPedId(), dict, "givetake2_a", 0.5) - StopAnimTask(data.ped, dict, "givetake2_b", 0.5) - if data.onBack then - data.onBack() - end -end - ---- Server event handler for processing item sales. ---- Removes sold items from inventory and funds the player based on the sale. -RegisterNetEvent(getScript().."Sellitems", function(data) - local src = source - local hasItems, hasTable = hasItem(data.item, 1, src) - if hasItems then - removeItem(data.item, hasTable[data.item].count, src) - fundPlayer((hasTable[data.item].count * data.price), "cash", src) - else - triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error", src) - end -end) - -------------------------------------------------------------- --- Shop Interface -------------------------------------------------------------- - ---- Opens a shop interface for the player. ---- ---- Checks job/gang restrictions, then uses the active inventory system to open the shop. ---- @param data table Contains: ---- - shop (`string`) The shop identifier. ---- - items (`table`) The items available in the shop. ---- - coords (`vector3`) where the shop is located. ---- - job/gang (optional) (`string`) Job or gang requirements. ----@usage ---- ```lua ---- openShop({ ---- shop = "weapon_shop", ---- items = weaponShopItems, ---- coords = vector3(100.0, 200.0, 300.0), ---- job = "police", ---- }) ---- ``` -function openShop(data) - jsonPrint(data) - if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end - - if Config.General.JimShops then - TriggerServerEvent("jim-shops:ShopOpen", "shop", data.items.label, data.items) - - elseif isStarted(OXInv) then - exports[OXInv]:openInventory('shop', { type = data.shop }) - - elseif isStarted(QSInv) then - TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) - - elseif isStarted(TgiannInv) then - TriggerServerEvent(getScript()..':server:openServerShop', data.shop) - - elseif isStarted(CodeMInv) then - --print(data.shop) - TriggerEvent("codem-inventory:openshop", data.shop) - - elseif isStarted(QBInv) then - TriggerServerEvent(getScript()..':server:openServerShop', data.shop) - TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) - - elseif isStarted(PSInv) then - TriggerServerEvent(getScript()..':server:openServerShop', data.shop) - TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items) - - elseif isStarted(RSGInv) then - TriggerServerEvent(getScript()..':server:openServerShop', data.shop) - end - lookEnt(data.coords) -end - -RegisterNetEvent(getScript()..':server:openServerShop', function(data) - if isStarted(QBInv) and checkExportExists(QBInv, "OpenShop") then - exports[QBInv]:OpenShop(source, data) - end - if isStarted(PSInv) and checkExportExists(PSInv, "OpenShop") then - exports[PSInv]:OpenShop(source, data) - end - if isStarted(TgiannInv) then - exports[TgiannInv]:OpenShop(source, data) - end - if isStarted(RSGInv) then - exports[RSGInv]:OpenShop(source, data) - end -end) - ---- Registers a shop with the active inventory system. ---- Supports either OXInv or QBInv (with QBInvNew flag). ---- ---- @param name string Unique shop identifier. ---- @param label string Display name for the shop. ---- @param items table List of available shop items. ---- @param society string|nil (Optional) Society identifier for shared shops. ---- @usage ---- ```lua ---- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons") ---- ``` -function registerShop(name, label, items, society) - local shopResource = "" - if isStarted(OXInv) then - shopResource = OXInv - exports[OXInv]:RegisterShop(name, { - name = label, - inventory = items, - society = society, - }) - - elseif isStarted(TgiannInv) then - shopResource = TgiannInv - exports[TgiannInv]:RegisterShop(name, items) - - elseif isStarted(QBInv) and checkExportExists(QBInv, "CreateShop") then - shopResource = QBInv - exports[QBInv]:CreateShop({ - name = name, - label = label, - slots = #items, - items = items, - society = society, - }) - - elseif isStarted(PSInv) and checkExportExists(PSInv, "CreateShop") then - shopResource = PSInv - exports[PSInv]:CreateShop({ - name = name, - label = label, - slots = #items, - items = items, - society = society, - }) - - elseif isStarted(RSGInv) then - shopResource = RSGInv - exports[RSGInv]:CreateShop({ - name = name, - label = label, - slots = #items, - items = items, - society = society, - }) - end - if shopResource ~= "" then - debugPrint("^6Bridge^7: ^2Registering ^5"..shopResource.." ^3Store^7:", name, "^4Label^7: "..label) - else - -- debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register shop^7:", name) - end -end \ No newline at end of file diff --git a/shared/stashcontrol.lua b/shared/stashcontrol.lua deleted file mode 100644 index 329fbf0..0000000 --- a/shared/stashcontrol.lua +++ /dev/null @@ -1,707 +0,0 @@ ---[[ - Stash Management Module - ------------------------- - This module handles stash-related operations including: - • Retrieving stash items (from server or local cache). - • Checking for required items in stashes. - • Opening stashes using different inventory systems. - • Removing items from stashes. - • Checking if a stash has specific items. -]] - --- Global variable to hold the current stash (used in callbacks). -local stash - --- If running on the server, create a callback to retrieve stash items. -if isServer() then - createCallback(getScript()..':server:GetStashItems', function(source, stashName) - if stashName == nil or stashName == "" then - return {} - end - stash = getStash(stashName) - return stash - end) -end - --- Local cache for stashes. -local stashCache = {} - ---- Retrieves (or updates) a local stash cache entry with a timeout. ---- When the cache is empty or expired, it triggers a server callback to update the items. ---- ---- @param stashName string The name of the stash. ---- @param stop boolean (Optional) If true, clears the entire stash cache. ---- @return boolean True if items exist in cache (and recheck is skipped), false otherwise. ---- ---- @usage ---- ```lua ---- local cached = GetStashTimeout("playerStash") ---- ``` -function GetStashTimeout(stashName, stop) - if stop or (stashName == nil or stashName == "") then - stashCache = {} - return false - end - - -- Retrieve cache for this stash, or initialize if not present. - stash = stashCache[stashName] - if not stash then - debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache ^1not ^2found^7, ^2need to grab from server^7") - stashCache[stashName] = { items = {}, timeout = 0 } - stash = stashCache[stashName] - else - debugPrint("^6Bridge^7: ^2Local Stash for ^7'^3"..stashName.."^7'^2 cache found^7") - end - - -- If there are already items in cache, skip recheck. - if countTable(stashCache[stashName].items) > 0 then - debugPrint("^6Bridge^7: '^3"..stashName.."^7' ^2Items found in local cache, skipping server recheck") - return true - end - - -- If timeout has expired, update the stash items from the server. - if stashCache[stashName].timeout <= 0 then - stashCache[stashName].items = triggerCallback(getScript()..':server:GetStashItems', stashName) - stashCache[stashName].timeout = 10000 -- Timeout in milliseconds. - CreateThread(function() - while stashCache[stashName] and stashCache[stashName].timeout > 0 do - stashCache[stashName].timeout -= 1000 - Wait(1000) - end - debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache timed out^7, ^3Clearing^7") - stashCache[stashName] = nil - end) - end - return false -end - ---- Checks if the specified stashes have the required items. ---- ---- If multiple stashes are provided (as a table), it iterates over each until all required items are found. ---- ---- @param stashes string|table Either a single stash name or a table of stash names. ---- @param itemTable table A table where keys are item names and values are the required amounts. ---- @return boolean, string|nil `boolean, string` true and the stash name if found, otherwise false and nil. ---- ---- @usage ---- ```lua ---- local found, stashName = checkStashItem({"playerStash", "storageStash"}, { iron = 2, wood = 5 }) ---- ``` -function checkStashItem(stashes, itemTable) - if not stashes or stashes == "" then - return hasItem(itemTable), nil - end - - if type(stashes) == "table" then - debugPrint("^6Bridge^7: ^2Checking multiple stashes for ingredients^7") - -- Iterate over each provided stash name. - for _, name in pairs(stashes) do - GetStashTimeout(name) - if stashhasItem(stashCache[name].items, itemTable, nil) then - return true, name - end - end - else - debugPrint("^6Bridge^7: ^2Checking "..(stashes and " '^3"..stashes.."^7'" or "").." ingredients") - GetStashTimeout(stashes) - return stashhasItem(stashCache[stashes].items, itemTable), stashes - end - - return false, nil -end - -------------------------------------------------------------- --- Stash Opening Functions -------------------------------------------------------------- - ---- Opens a stash using the active inventory system. ---- ---- Checks for job or gang restrictions before opening the stash. ---- ---- @param data table A table containing stash data: ---- - stash (string): The stash identifier. ---- - label (string): Display label. ---- - maxWeight (number|nil): Maximum weight (default 600000). ---- - slots (number|nil): Number of slots (default 40). ---- - stashOptions (table|nil): Additional options for the stash. ---- - job/gang (string|nil): Restriction for access. ---- - coords (vector3): Coordinates to "look" at. ---- ---- @usage ---- ```lua ---- openStash({ stash = "playerStash", label = "Player Stash", coords = vector3(100, 200, 30) }) ---- ``` -function openStash(data) - if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end - - if isStarted(OXInv) then - exports[OXInv]:openInventory('stash', data.stash) - - elseif isStarted(CoreInv) then - TriggerServerEvent('core_inventory:server:openInventory', data.stash, 'stash') - - elseif isStarted(CodeMInv) then - TriggerServerEvent('codem-inventory:server:openstash', data.stash, data.slots, data.maxWeight, data.label) - - elseif isStarted(OrigenInv) then - exports[OrigenInv]:openInventory('stash', data.stash, { label = data.label }) - - elseif isStarted(TgiannInv) then - TriggerServerEvent(getScript()..':server:openServerStash', { - stashName = data.stash, - label = data.label, - maxweight = data.maxWeight or 600000, - slots = data.slots or 40 - }) - - elseif isStarted(JPRInv) then - if QBInvNew then - TriggerServerEvent(getScript()..':server:openServerStash', { - stashName = data.stash, - label = data.label, - maxweight = data.maxWeight or 600000, - slots = data.slots or 40 - }) - else - TriggerEvent("inventory:client:SetCurrentStash", data.stash) - TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { - slots = data.slots or 50, - maxWeight = data.maxWeight or 600000 - }) - end - - elseif isStarted(QBInv) then - if QBInvNew then - TriggerServerEvent(getScript()..':server:openServerStash', { - stashName = data.stash, - label = data.label, - maxweight = data.maxWeight or 600000, - slots = data.slots or 40 - }) - else - TriggerEvent("inventory:client:SetCurrentStash", data.stash) - TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { - slots = data.slots or 50, - maxWeight = data.maxWeight or 600000 - }) - end - - elseif isStarted(PSInv) then - if QBInvNew then - TriggerServerEvent(getScript()..':server:openServerStash', { - stashName = data.stash, - label = data.label, - maxweight = data.maxWeight or 600000, - slots = data.slots or 40 - }) - else - TriggerEvent("ps-inventory:client:SetCurrentStash", data.stash) - TriggerEvent("inventory:client:SetCurrentStash", data.stash) - TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { - slots = data.slots or 50, - maxWeight = data.maxWeight or 600000 - }) - end - - elseif isStarted(RSGInv) then - TriggerServerEvent(getScript()..':server:openServerStash', { - stashName = data.stash, - label = data.label, - maxweight = data.maxWeight or 600000, - slots = data.slots or 40 - }) - - else - --Fallback to these commands - TriggerEvent("inventory:client:SetCurrentStash", data.stash) - TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, { - slots = data.slots or 50, - maxWeight = data.maxWeight or 600000 - }) - end - - lookEnt(data.coords) -end - - --- Wrapper function for opening stash from the server. --- Messy but not much else I can do about it. -RegisterNetEvent(getScript()..":server:openServerStash", function(data) - local src = source - if isStarted(TgiannInv) then - exports[TgiannInv]:OpenInventory(source, 'stash', data.stashName, data) - end - if isStarted(JPRInv) then - exports[JPRInv]:OpenInventory(source, data.stashName, data) - end - if isStarted(QBInv) then - exports[QBInv]:OpenInventory(source, data.stashName, data) - end - if isStarted(PSInv) then - exports[PSInv]:OpenInventory(source, data.stashName, data) - end - if isStarted(RSGInv) then - exports[RSGInv]:OpenInventory(source, data.stashName, data) - end -end) - -function clearStash(stashId) - if isStarted(JPRInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..JPRInv.."^2 Stash^7", stashId) - MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { - ['stash'] = stashId, - ['items'] = json.encode({}) - }) - - elseif isStarted(QBInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..QBInv.."^2 Stash^7", stashId) - if QBInvNew then - exports[QBInv]:ClearStash(stashId) - else - MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { - ['stash'] = stashId, - ['items'] = json.encode({}) - }) - end - - elseif isStarted(OXInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..OXInv.."^2 Stash^7", stashId) - exports[OXInv]:ClearInventory(stashId) - - elseif isStarted(PSInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..PSInv.."^2 Stash^7", stashId) - MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { - ['stash'] = stashId, - ['items'] = json.encode({}) - }) - - elseif isStarted(QSInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..QSInv.."^2 Stash^7", stashId) - exports[QSInv]:ClearOtherInventory('stash', stashId) - - elseif isStarted(CoreInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..CoreInv.."^2 Stash^7", stashId) - exports[CoreInv]:clearInventory("stash-"..stashId) - - elseif isStarted(CodeMInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..CodeMInv.."^2 Stash^7", stashId) - exports[CodeMInv]:ClearInventory(stashId) - - elseif isStarted(OrigenInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..OrigenInv.."^2 Stash^7", stashId) - exports[OrigenInv]:ClearInventory(stashId) - - elseif isStarted(TgiannInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..TgiannInv.."^2 Stash^7", stashId) - exports["tgiann-inventory"]:DeleteInventory("stash", stashId) - - elseif isStarted(RSGInv) then - debugPrint("^5Bridge^7: ^2Clearing ^3"..RSGInv.."^2 Stash^7", stashId) - exports[RSGInv]:ClearStash(stashId) - - end -end - - -------------------------------------------------------------- --- Stash Retrieval Function -------------------------------------------------------------- - ---- Retrieves stash items from the active inventory system. ---- ---- This function converts the raw stash items into a standardized table using the global Items lookup. ---- ---- @param stashName string The identifier for the stash. ---- @return stashTable table A table of items from the stash. ---- ---- @usage ---- ```lua ---- local items = getStash("playerStash") ---- ``` -function getStash(stashName) - local stashResource = "" - if stashName == "" or type(stashName) ~= "string" then - print("^6Bridge^7: ^2Stash name was not a string ^3"..stashName.."^7(^3"..type(stashName).."^7)") - return {} - end - - local stashItems, items = {}, {} - if isStarted(OXInv) then - stashResource = OXInv - local stash = exports[OXInv]:Inventory(stashName) - -- Add fallback if ox can't find the stash and returns a boolean - stashItems = type(stash) == "table" and stash.items or {} - - elseif isStarted(QSInv) then - stashResource = QSInv - stashItems = exports[QSInv]:GetStashItems(stashName) - - elseif isStarted(CoreInv) then - stashResource = CoreInv - stashItems = exports[CoreInv]:getInventory(stashName) - - elseif isStarted(CodeMInv) then - stashResource = CodeMInv - stashItems = exports[CodeMInv]:GetStashItems(stashName) - - elseif isStarted(OrigenInv) then - stashResource = OrigenInv - stashItems = exports[OrigenInv]:getInventory(stashName) - - elseif isStarted(TgiannInv) then - stashResource = TgiannInv - stashItems = exports[TgiannInv]:GetSecondaryInventoryItems("stash", stashName) - - elseif isStarted(PSInv) then - stashResource = PSInv - local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName }) - if result then stashItems = json.decode(result) end - - elseif isStarted(JPRInv) then - stashResource = JPRInv - local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName }) - if result then stashItems = json.decode(result) end - - elseif isStarted(QBInv) then - stashResource = QBInv - if QBInvNew then - local result = exports[QBInv]:GetInventory(stashName) or {} - stashItems = result.items or {} - else - local result = MySQL.scalar.await("SELECT items FROM stashitems WHERE stash = ?", { stashName }) - if result then stashItems = json.decode(result) end - end - - elseif isStarted(RSGInv) then - stashResource = RSGInv - stashItems = exports[RSGInv]:GetInventory(stashName) - - end - - debugPrint("^6Bridge^7: ^2Retrieving ^3Stash^2 with ^7"..stashResource) - if stashItems then - for _, item in pairs(stashItems) do - local itemInfo = Items[item.name:lower()] - if itemInfo then - local indexNum = #items + 1 -- Fallback index if slot is missing. - items[(item.slot or indexNum)] = { - name = itemInfo.name or nil, - amount = tonumber(item.amount) or tonumber(item.count), - info = item.info or "", - label = itemInfo.label or nil, - description = itemInfo.description or "", - weight = itemInfo.weight or nil, - type = itemInfo.type or nil, - unique = itemInfo.unique or nil, - useable = itemInfo.useable or nil, - image = itemInfo.image or nil, - slot = (item.slot and item.slot) or indexNum, - metadata = (item.metadata and item.metadata) or nil, - } - end - end - debugPrint("^6Bridge^7: ^3GetStashItems^7: ^2Stash information for '^6"..stashName.."^7' retrieved") - end - jsonPrint(items) - return items -end - -------------------------------------------------------------- --- Stash Item Removal Function -------------------------------------------------------------- - ---- Removes items from a stash using the active inventory system. ---- ---- Iterates over the provided items and adjusts the stash contents accordingly. ---- ---- @param stashItems table The current stash items. ---- @param stashName string|table The stash identifier (or table of identifiers). ---- @param items table A table of items to remove (keys are item names, values are amounts). ---- ---- @usage ---- ```lua ---- stashRemoveItem(currentItems, "playerStash", { iron = 2, wood = 5 }) ---- ``` -function stashRemoveItem(stashItems, stashName, items) - if stashName == "" or stashName == nil then - print("^1ERROR^7: ^1stashRemoveItem triggered but stashName was empty^7") - return - end - if type(stashName) ~= "table" then - stashName = { stashName } - end - - if isStarted(OXInv) then - for k, v in pairs(items) do - for _, name in pairs(stashName) do - local success = exports[OXInv]:RemoveItem(name, k, v) - if success then - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OXInv, k, v) - break - end - end - end - - elseif isStarted(QSInv) then - for k, v in pairs(items) do - exports[QSInv]:RemoveItemIntoStash(stashName[1], k, v) - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QSInv, k, v) - end - - elseif isStarted(CoreInv) then - for k, v in pairs(items) do - exports[CoreInv]:removeItemExact(stashName[1], k, v) - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..CoreInv, k, v) - end - - elseif isStarted(CodeMInv) then - for k, v in pairs(items) do - for l in pairs(stashItems) do - if stashItems[l].name == k then - if (stashItems[l].amount - v) <= 0 then - debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v) - stashItems[l] = nil - else - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..CodeMInv, k, v) - stashItems[l].amount -= v - end - end - end - end - exports[CodeMInv]:UpdateStash(stashName[1], stashItems) - debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3CodeM^2 stash ^7'^6"..stashName[1].."^7'") - - elseif isStarted(OrigenInv) then - for k, v in pairs(items) do - exports[OrigenInv]:RemoveFromStash(stashName[1], k, v) - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OrigenInv, k, v) - end - - elseif isStarted(TgiannInv) then - for k, v in pairs(items) do - local itemData = exports[TgiannInv]:GetItemByNameFromSecondaryInventory("stash", stashName[1], k) - exports[TgiannInv]:RemoveItemFromSecondaryInventory("stash", stashName[1], k, v, itemData.slot, nil) - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..TgiannInv, k, v) - end - - elseif isStarted(JPRInv) then - if not stashItems or not next(stashItems) then - stashItems = getStash(stashName[1]) - end - for k, v in pairs(items) do - for l in pairs(stashItems) do - if stashItems[l].name == k then - if (stashItems[l].amount - v) <= 0 then - debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v) - stashItems[l] = nil - else - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with "..JPRInv, k, v) - stashItems[l].amount -= v - end - end - end - end - debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3JPR^2 stash '^6"..stashName[1].."^7'") - MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { - ['stash'] = stashName[1], - ['items'] = json.encode(stashItems) - }) - - elseif isStarted(QBInv) then - if QBInvNew then - for k, v in pairs(items) do - exports[QBInv]:RemoveItem(stashName[1], k, v, false, 'crafting') - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v) - end - else - if not stashItems or not next(stashItems) then - stashItems = getStash(stashName[1]) - end - for k, v in pairs(items) do - for l in pairs(stashItems) do - if stashItems[l].name == k then - if (stashItems[l].amount - v) <= 0 then - debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v) - stashItems[l] = nil - else - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with "..QBInv, k, v) - stashItems[l].amount -= v - end - end - end - end - debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash '^6"..stashName[1].."^7'") - MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { - ['stash'] = stashName[1], - ['items'] = json.encode(stashItems) - }) - end - - elseif isStarted(PSInv) then - if not stashItems or not next(stashItems) then - stashItems = getStash(stashName[1]) - end - for k, v in pairs(items) do - for l in pairs(stashItems) do - if stashItems[l].name == k then - if (stashItems[l].amount - v) <= 0 then - debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v) - stashItems[l] = nil - else - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..PSInv, k, v) - stashItems[l].amount -= v - end - end - end - end - debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3PS^2 stash ^7'^6"..stashName[1].."^7'") - MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { - ['stash'] = stashName[1], - ['items'] = json.encode(stashItems) - }) - - elseif isStarted(RSGInv) then - for k, v in pairs(items) do - exports[RSGInv]:RemoveItem(stashName[1], k, v, false, 'crafting') - debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..RSGInv, k, v) - end - else - print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3starter^1.^2lua^7") - end -end -RegisterNetEvent(getScript()..":server:stashRemoveItem", stashRemoveItem) - -------------------------------------------------------------- --- Stash Item Availability Check -------------------------------------------------------------- - ---- Checks whether a stash has the required amount of specific items. ---- ---- It iterates through the provided items and tallies available quantities. ---- ---- @param stashItems table The items available in the stash. ---- @param items string|table The item name or table of required items (key: item, value: amount). ---- @param amount number (Optional) The required amount (if a single item is provided). ---- @return boolean, table (`boolean, string`) Returns true (and a table with counts) if all items are available; false otherwise. ---- ---- @usage ---- ```lua ---- local hasAll, details = stashhasItem(currentStashItems, { iron = 2, wood = 5 }) ---- ``` -function stashhasItem(stashItems, items, amount) - local invs = { OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv, JPRInv, QBInv, PSInv, RSGInv } - local foundInv = "" - for _, inv in ipairs(invs) do - if isStarted(inv) then - foundInv = inv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6") - break - end - end - - -- Ensure items is a table. - if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end - - local hasTable = {} - for item, requiredAmount in pairs(items) do - local count = 0 - for _, itemData in pairs(stashItems) do - if itemData and (itemData.name == item) then - count += (itemData.amount or 1) - end - end - - local debugMsg = string.format("^6Bridge^7: ^3stashHasItem^7[^6%s^7]: %s '%s' ^3%d^7/^3%d^7", foundInv, (count >= requiredAmount and "^5FOUND^7" or "^1NOT FOUND^7"), item, count, requiredAmount) - debugPrint(debugMsg) - - hasTable[item] = { hasItem = (count >= requiredAmount), count = count } - end - - for k, v in pairs(hasTable) do if v.hasItem == false then return false, hasTable end end - - return true, hasTable -end - - ---- Registers a stash with the active inventory system. ---- Supports either OXInv or QSInv. ---- ---- @param name string Unique stash identifier. ---- @param label string Display name for the stash. ---- @param slots number|nil (Optional) Number of slots (default 50). ---- @param weight number|nil (Optional) Maximum weight (default 4000000). ---- @param owner string|nil (Optional) Owner identifier for personal stashes. ---- @param coords table|nil (Optional) Coordinates for the stash location. ---- @usage ---- ```lua ---- registerStash( ---- "playerStash", ---- "Player Stash", ---- 100, ---- 8000000, ---- "player123", ---- { x = 100.0, y = 200.0, z = 30.0 } ---- ) ---- ``` -function registerStash(name, label, slots, weight, owner, coords) - local stashResource = "" - if isStarted(OXInv) then - stashResource = OXInv - exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil) - - elseif isStarted(OrigenInv) then - stashResource = OrigenInv - exports["origen_inventory"]:registerStash(name, label, slots or 50, weight or 4000000) - - elseif isStarted(TgiannInv) then - stashResource = TgiannInv - exports[TgiannInv]:RegisterStash(name, label, slots or 50, weight or 4000000) - - end - if stashResource ~= "" then - debugPrint("^6Bridge^7: ^2Registering ^4"..stashResource.." ^3Stash^7:", name, "^4Label^7: "..label) - else - debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register stash^7:", name) - end -end - -if isServer() then - --- Registers an event to create an OX stash from the server. - --- When triggered, it calls registerStash with the provided parameters. - --- - --- @event server:makeOXStash - --- @param name string Unique stash identifier. - --- @param label string Display name for the stash. - --- @param slots number|nil (Optional) Number of slots. - --- @param weight number|nil (Optional) Maximum weight. - --- @param owner string|nil (Optional) Owner identifier. - --- @param coords table|nil (Optional) Stash coordinates. - --- @usage - --- ```lua - --- TriggerEvent(getScript()..":server:makeOXStash", name, label, slots, weight, owner, coords, token) - --- ``` - RegisterNetEvent(getScript()..":server:makeOXStash", function(name, label, slots, weight, owner, coords, token) - local src = source or nil - if src then - if not checkToken(src, token, "stash", name) then - return - end - end - - registerStash(name, label, slots, weight, owner, coords) - end) -end - -RegisterNetEvent(getScript()..":openGrabBox", function(data) - if isStarted(OXInv) then - return - end - local id = "" - if data.metadata then - id = data.metadata.id - elseif data.info then - id = data.info.id - end - openStash({ - stash = id, - }) -end) \ No newline at end of file diff --git a/starter.lua b/starter.lua index 97ccf21..f990d99 100644 --- a/starter.lua +++ b/starter.lua @@ -149,7 +149,6 @@ for _, v in pairs({ -- This is a specific load order 'wrapperfunctions.lua', 'polyZone.lua', - 'inventories.lua', 'itemcontrol.lua', 'playerfunctions.lua', 'metaHandlers.lua', @@ -167,8 +166,6 @@ for _, v in pairs({ -- This is a specific load order -- Crafting / Shops / Stashes 'crafting.lua', - 'shops.lua', - 'stashcontrol.lua', -- Kind of "other" 'isAnimal.lua',