mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-17 14:06:02 +01:00
This removes redundant inventory files and compiles all of the functions into one This allows easier editing and control per inventory and hopefully better optimization, readability and easier addition of support for new inventories
2297 lines
86 KiB
Lua
2297 lines
86 KiB
Lua
--[[
|
|
Usable Items & Inventory Utilities Module
|
|
-------------------------------------------
|
|
This module provides functions for:
|
|
• Registering items as usable across different inventory systems (ESX, QBcore, QBX).
|
|
• Retrieving an item's image as a NUI link.
|
|
• Adding and removing items from a player's inventory.
|
|
• Toggling items in inventory (with server event for exploit protection).
|
|
• Checking for item duplication exploits.
|
|
• Handling tool durability mechanics.
|
|
• Checking item availability and retrieving inventory.
|
|
• Granting random rewards from a reward pool.
|
|
• Checking if a player can carry specific items based on weight.
|
|
]]
|
|
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
|
|
-------------------------------------------------------------
|
|
|
|
--- Registers an item as usable for ESX, QBcore, or QBX.
|
|
---
|
|
--- @param item string The name of the item.
|
|
--- @param funct function The function to execute when the item is used.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- createUseableItem("health_potion", function(source)
|
|
--- -- Code to consume the health potion
|
|
--- end)
|
|
--- ```
|
|
function createUseableItem(item, funct)
|
|
if doesItemExist(item) then
|
|
local itemResource = ""
|
|
if isStarted(ESXExport) then
|
|
itemResource = ESXExport
|
|
while not ESX do Wait(0) end
|
|
ESX.RegisterUsableItem(item, funct)
|
|
|
|
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
|
itemResource = QBExport
|
|
Core.Functions.CreateUseableItem(item, funct)
|
|
|
|
elseif isStarted(QBXExport) then
|
|
itemResource = QBXExport
|
|
exports[QBXExport]:CreateUseableItem(item, funct)
|
|
|
|
elseif isStarted(RSGExport) then
|
|
itemResource = RSGExport
|
|
Core.Functions.CreateUseableItem(item, funct)
|
|
|
|
end
|
|
|
|
if itemResource ~= "" then
|
|
debugPrint("^6Bridge^7: ^2Registering ^3UsableItem^2 with ^4"..itemResource.."^7:", item)
|
|
else
|
|
debugPrint("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
|
|
end
|
|
else
|
|
print("^1ERROR^7: ^1Tried to make item usable but it didn't exist^7: "..item)
|
|
end
|
|
end
|
|
|
|
|
|
-------------------------------------------------------------
|
|
-- Adding and Removing Items
|
|
-------------------------------------------------------------
|
|
|
|
--- Adds an item to a player's inventory.
|
|
---
|
|
--- Triggers a server event (or local event) to add the specified item.
|
|
---
|
|
--- @param item string The item name.
|
|
--- @param amount number The quantity to add.
|
|
--- @param info table|nil Additional metadata for the item.
|
|
--- @param src number|nil Optional player source; if nil, defaults to the caller.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- addItem("health_potion", 2, { quality = "high" })
|
|
--- ```
|
|
function addItem(item, amount, info, src)
|
|
if not Items[item] then
|
|
print("^6Bridge^7: ^1Error^7 - ^2Tried to give ^7'^3"..item.."^7'^2 but it doesn't exist")
|
|
return
|
|
end
|
|
|
|
if src then
|
|
TriggerEvent(getScript()..":server:toggleItem", true, item, amount, src, info)
|
|
else
|
|
local timeout = GetGameTimer() + 5000
|
|
while currentToken == nil and GetGameTimer() < timeout do Wait(100) end
|
|
TriggerServerEvent(getScript()..":server:toggleItem", true, item, amount, nil, info, nil, currentToken)
|
|
currentToken = nil -- clear client cached token
|
|
end
|
|
end
|
|
|
|
--- Removes an item from a player's inventory.
|
|
---
|
|
--- Triggers a server event (or local event) to remove the specified item.
|
|
---
|
|
--- @param item string The item name.
|
|
--- @param amount number The quantity to remove.
|
|
--- @param src number|nil Optional player source.
|
|
--- @param slot number|nil Optional inventory slot.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- removeItem("health_potion", 1)
|
|
--- ```
|
|
function removeItem(item, amount, src, slot)
|
|
if not Items[item] then
|
|
print("^6Bridge^7: ^1Error^7 - ^2Tried to remove ^7'^3"..item.."^7'^2 but it doesn't exist")
|
|
return
|
|
end
|
|
|
|
if src then
|
|
--debugPrint(src)
|
|
TriggerEvent(getScript()..":server:toggleItem", false, item, amount, src, nil, slot)
|
|
else
|
|
TriggerServerEvent(getScript()..":server:toggleItem", false, item, amount, nil, nil, slot)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Toggle Items (Server Event)
|
|
-------------------------------------------------------------
|
|
|
|
--- Server event handler to toggle (add or remove) an item from a player's inventory.
|
|
---
|
|
--- This function validates the item, then calls the appropriate export functions based on the active inventory system.
|
|
--- It also includes exploit protection via the dupeWarn function.
|
|
---
|
|
--- @param give boolean True to add the item, false to remove.
|
|
--- @param item string The item name.
|
|
--- @param amount number The quantity.
|
|
--- @param newsrc number|nil The player source; defaults to event source.
|
|
--- @param info table|nil Additional metadata.
|
|
--- @param slot number|nil Optional inventory slot.
|
|
---
|
|
--- @usage
|
|
--- ```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 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
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local action = (tostring(give) == "true" and "addItem" or "removeItem")
|
|
local remamount = amount or 1
|
|
if item == nil then return end
|
|
|
|
local invName = ""
|
|
if give == 0 or give == false then
|
|
if not hasItem(item, amount or 1, src) then
|
|
dupeWarn(src, item, amount)
|
|
|
|
else
|
|
for _, inv in ipairs(InvFunc) do
|
|
if isStarted(inv.invName) then
|
|
invName = inv.invName
|
|
inv.removeItem(src, item, remamount)
|
|
break
|
|
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)
|
|
|
|
elseif ESX and isStarted(ESXExport) then -- if esx then use core functions
|
|
invName = ESX
|
|
ESX.GetPlayerFromId(src).removeInventoryItem(item, remamount)
|
|
|
|
end
|
|
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
|
|
for _, inv in ipairs(InvFunc) do
|
|
if isStarted(inv.invName) then
|
|
invName = inv.invName
|
|
inv.addItem(src, item, amountToAdd, info, slot)
|
|
break
|
|
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)
|
|
|
|
elseif ESX and isStarted(ESXExport) then -- if esx then use core functions
|
|
invName = ESX
|
|
ESX.GetPlayerFromId(src).addInventoryItem(item, amountToAdd)
|
|
end
|
|
else
|
|
debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1))
|
|
end
|
|
end
|
|
end)
|
|
|
|
-------------------------------------------------------------
|
|
-- Exploit Protection
|
|
-------------------------------------------------------------
|
|
|
|
--- Warns and kicks a player if they try to remove an item they don't have.
|
|
---
|
|
--- @param src number The player's source ID.
|
|
--- @param item string The item name.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- dupeWarn(playerId, "health_potion")
|
|
--- ```
|
|
function dupeWarn(src, item, message)
|
|
local name = getPlayer(src).name
|
|
print(message or "^5DupeWarn^7: "..name.." (^1"..tostring(src).."^7) ^2Tried to remove item '^3"..item.."^7'^2 but it wasn't there^7")
|
|
if not debugMode then
|
|
DropPlayer(src, name.."("..tostring(src)..") kicked by exploit protection")
|
|
end
|
|
print("^5DupeWarn^7: "..name.."(^1"..tostring(src).."^7) ^2Dropped from server - exploit protection^7")
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Tool Durability & Metadata
|
|
-------------------------------------------------------------
|
|
|
|
--- Reduces the durability of a tool by a specified damage amount.
|
|
---
|
|
--- If durability reaches zero or below, the tool is removed and a break sound is played.
|
|
---
|
|
--- @param data table Contains:
|
|
--- - item (string): The tool's name.
|
|
--- - damage (number): The damage % to apply.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- breakTool({ item = "drill", damage = 10 })
|
|
--- ```
|
|
function breakTool(data)
|
|
local metadata, slot = getItemMetadata(data.item)
|
|
metadata = metadata or {}
|
|
if not metadata.durability then metadata.durability = 100 end
|
|
metadata.durability -= data.damage
|
|
if metadata.durability <= 0 then
|
|
removeItem(data.item, 1, nil, slot)
|
|
local breakId = GetSoundId()
|
|
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
|
|
else
|
|
TriggerServerEvent(getScript()..":server:setItemMetaData", { item = data.item, slot = slot, metadata = metadata })
|
|
end
|
|
end
|
|
|
|
|
|
--- Reduces the uses of a tool by a specified damage amount.
|
|
---
|
|
--- If uses reaches zero or below, the tool is removed and a break sound is played.
|
|
---
|
|
--- @param data table Contains:
|
|
--- - item (string): The tool's name.
|
|
--- - damage (number): The damage % to apply.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- useToolDegrade({ item = "drill", maxUse = 10 })
|
|
--- ```
|
|
|
|
function useToolDegrade(data) -- WIP
|
|
local metadata, slot = getItemMetadata(data.item)
|
|
metadata = metadata or {}
|
|
if not metadata["Uses Left"] then metadata["Uses Left"] = data.maxUse end
|
|
metadata["Uses Left"] -= 1
|
|
if metadata["Uses Left"] <= 0 then
|
|
removeItem(data.item, 1, nil, slot)
|
|
local breakId = GetSoundId()
|
|
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
|
|
else
|
|
TriggerServerEvent(getScript()..":server:setItemMetaData", { item = data.item, slot = slot, metadata = metadata })
|
|
end
|
|
end
|
|
|
|
-- Grab whole inventory and check for metadata
|
|
function getItemMetadata(item, slot, src)
|
|
local lowestSlot = 100
|
|
local chosenSlot = slot
|
|
local metadata = {}
|
|
|
|
local itemcheck = getPlayerInv(src)
|
|
|
|
if chosenSlot then
|
|
for k, v in pairs(itemcheck) do
|
|
if v.name == item and v.slot == chosenSlot then
|
|
lowestSlot = v.slot
|
|
metadata = itemcheck[k].info or itemcheck[k].metadata or {}
|
|
debugPrint("^6Bridge^7: ^2Found metadata for item ^3"..item.." ^2in slot ^3"..lowestSlot.."^7")
|
|
end
|
|
end
|
|
else
|
|
for k, v in pairs(itemcheck) do
|
|
if v.name == item then
|
|
if v.slot <= lowestSlot then
|
|
lowestSlot = v.slot
|
|
metadata = itemcheck[k].info or itemcheck[k].metadata or {}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return metadata, lowestSlot
|
|
end
|
|
|
|
--- Server event handler to set metadata for an item.
|
|
---
|
|
--- Updates item metadata (e.g. durability) for the player's inventory based on slot.
|
|
---
|
|
--- @param data table Contains:
|
|
--- - item (string): The item name.
|
|
--- - slot (number): The inventory slot.
|
|
--- - metadata (table): The metadata to set.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- TriggerServerEvent("script:server:setItemMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } })
|
|
--- ```
|
|
RegisterNetEvent(getScript()..":server:setItemMetaData", function(data, src)
|
|
local src = src or source
|
|
for _, inv in pairs(InvFunc) do
|
|
if isStarted(inv.invName) then
|
|
inv.setItemMetadata(data, src)
|
|
break
|
|
end
|
|
end
|
|
end)
|
|
|
|
-------------------------------------------------------------
|
|
-- Random Reward
|
|
-------------------------------------------------------------
|
|
|
|
--- Grants a random reward from a predefined reward pool if the player is eligible.
|
|
---
|
|
--- Checks if the item qualifies for a reward, removes the item, then calculates a random reward based on rarity.
|
|
---
|
|
--- @param itemName string The item name to check.
|
|
---
|
|
---@usage
|
|
--- ```lua
|
|
--- getRandomReward("gold_ring")
|
|
--- ```
|
|
function getRandomReward(itemName)
|
|
if Config.Rewards.RewardPool then
|
|
local reward = false
|
|
if type(Config.Rewards.RewardItem) == "string" then
|
|
Config.Rewards.RewardItem = { Config.Rewards.RewardItem }
|
|
end
|
|
for k, v in pairs(Config.Rewards.RewardItem) do
|
|
if v == itemName then
|
|
reward = true
|
|
break
|
|
end
|
|
end
|
|
if reward then
|
|
removeItem(itemName, 1)
|
|
local totalRarity = 0
|
|
for i = 1, #Config.Rewards.RewardPool do
|
|
totalRarity += Config.Rewards.RewardPool[i].rarity
|
|
end
|
|
debugPrint("^6Bridge^7: ^3getRandomReward^7: Total Rarity '"..totalRarity.."'")
|
|
local randomNum = math.random(1, totalRarity)
|
|
debugPrint("^6Bridge^7: ^3getRandomReward^7: Random Number '"..randomNum.."'")
|
|
local currentRarity = 0
|
|
for i = 1, #Config.Rewards.RewardPool do
|
|
currentRarity += Config.Rewards.RewardPool[i].rarity
|
|
if randomNum <= currentRarity then
|
|
debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Selected toy ^7'^6"..Config.Rewards.RewardPool[i].item.."^7'")
|
|
addItem(Config.Rewards.RewardPool[i].item, 1)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Carry Capacity Check
|
|
-------------------------------------------------------------
|
|
|
|
--- Checks if a player can carry the specified items based on weight.
|
|
---
|
|
--- Calculates the current total weight in the player's inventory and determines whether adding the new items would exceed capacity.
|
|
---
|
|
--- @param itemTable table A table where keys are item names and values are required quantities.
|
|
--- @param src number The player's source ID.
|
|
--- @return table A table mapping each item to a boolean indicating if it can be carried.
|
|
---
|
|
--- @usage
|
|
--- local carryCheck = canCarry({ ["health_potion"] = 2, ["mana_potion"] = 3 }, playerId)
|
|
--- if carryCheck["health_potion"] and carryCheck["mana_potion"] then
|
|
--- -- Player can carry items.
|
|
--- else
|
|
--- -- Notify player.
|
|
--- end
|
|
function canCarry(itemTable, src)
|
|
local resultTable = {}
|
|
for _, inv in pairs(InvFunc) do
|
|
if isStarted(inv.invName) then
|
|
resultTable = inv.canCarry(itemTable, src)
|
|
break
|
|
end
|
|
end
|
|
return resultTable
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Server Callback Registration
|
|
-------------------------------------------------------------
|
|
if isServer() then
|
|
createCallback(getScript()..":server:canCarry", function(source, itemTable)
|
|
local result = canCarry(itemTable, source)
|
|
return result
|
|
end)
|
|
|
|
createCallback(getScript()..":server:getMaxCarryCraft", function(source, data)
|
|
local src = source
|
|
local item = data.item
|
|
local max = data.max or 100
|
|
|
|
local maxCanCarry = 0
|
|
for i = 1, max do
|
|
local checkTable = {
|
|
[item] = i
|
|
}
|
|
local result = canCarry(checkTable, src)
|
|
if result[item] == true then
|
|
maxCanCarry = i
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return maxCanCarry
|
|
end)
|
|
end
|
|
|
|
function getMaxInvWeight()
|
|
local weight = 0
|
|
for _, inv in pairs(InvFunc) do
|
|
if isStarted(inv.invName) then
|
|
weight = inv.getMaxInvWeight()
|
|
break
|
|
end
|
|
end
|
|
return weight
|
|
end
|
|
|
|
function getCurrentInvWeight()
|
|
local weight = 0
|
|
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
|
|
local itemcheck = Core.Functions.GetPlayerData().items
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
|
|
if isStarted(OXInv) then
|
|
weight = exports[OXInv]:GetPlayerWeight()
|
|
end
|
|
|
|
if isStarted(QSInv) then
|
|
local itemcheck = exports[QSInv]:getUserInventory()
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
|
|
if isStarted(CoreInv) then
|
|
local itemcheck = exports[CoreInv]:getInventory()
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
|
|
if isStarted(CodeMInv) then
|
|
local itemcheck = exports[CodeMInv]:GetClientPlayerInventory()
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
|
|
if isStarted(OrigenInv) then
|
|
local itemcheck = exports[OrigenInv]:getPlayerInventory()
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
|
|
if isStarted(TgiannInv) then
|
|
local itemcheck = exports[TgiannInv]:GetPlayerItems()
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
|
|
-- For ESX default inventory (es_extended)
|
|
if ESX and isStarted(ESXExport) then
|
|
local xPlayer = ESX.GetPlayerData() or {}
|
|
if xPlayer.inventory then
|
|
for _, v in pairs(xPlayer.inventory) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
end
|
|
|
|
return weight
|
|
end
|
|
|
|
function hasFreeInventorySlots(slots, src)
|
|
local inv = getPlayerInv(src)
|
|
if (countTable(inv) + slots) <= InventorySlots then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Check Item Existance - return boolean(true,false)
|
|
-------------------------------------------------------------
|
|
|
|
--- checks whether an item exists in your item database
|
|
---
|
|
--- @param item string The item name.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- local item = "apple"
|
|
--- if doesItemExist(item) then print("item exists") end
|
|
--- ```
|
|
function doesItemExist(item)
|
|
if not item or item == "" then
|
|
return false
|
|
end
|
|
if not Items or not next(Items) then
|
|
return item.." (Missing)"
|
|
end
|
|
if Items[item] ~= nil then
|
|
return Items[item]
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
function getItemLabel(item)
|
|
if not item or item == "" then
|
|
return ""
|
|
end
|
|
if not Items or not next(Items) then
|
|
return item.." (Missing)"
|
|
end
|
|
if Items[item] ~= nil then
|
|
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 |