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',