mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-17 14:06:02 +01:00
2839 lines
105 KiB
Lua
2839 lines
105 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,
|
|
hasItem =
|
|
function(item, amount, src)
|
|
if src then
|
|
local serverItemCheck = exports[OXInv]:GetItem(src, item, nil, true) or 0
|
|
return serverItemCheck >= amount, serverItemCheck
|
|
else
|
|
local localItemCheck = exports[OXInv]:GetItemCount(item) or 0
|
|
return localItemCheck >= amount, localItemCheck
|
|
end
|
|
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()
|
|
return exports[OXInv]:GetPlayerMaxWeight()
|
|
end,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += v.weight
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
exports[OXInv]:openInventory('shop', { type = name })
|
|
end,
|
|
serverOpenShop = function(shopName)
|
|
--
|
|
end,
|
|
registerShop =
|
|
function(name, label, items, society)
|
|
exports[OXInv]:RegisterShop(name, {
|
|
name = label,
|
|
inventory = items,
|
|
society = society,
|
|
})
|
|
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)
|
|
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,
|
|
hasItem =
|
|
function(item, amount, src)
|
|
if src then
|
|
local serverItemCheck = exports[CoreInv]:getItemCount(src, item) or 0
|
|
return serverItemCheck >= amount, serverItemCheck
|
|
else
|
|
local localItemCheck = exports[CoreInv]:getItemCount(item) or 0
|
|
return localItemCheck >= amount, localItemCheck
|
|
end
|
|
end,
|
|
canCarry =
|
|
function(itemTable, src)
|
|
local resultTable = {}
|
|
for k, v in pairs(itemTable) do
|
|
resultTable[k] = exports[CoreInv]:canCarry(src, k, v)
|
|
end
|
|
return resultTable
|
|
end,
|
|
getMaxInvWeight =
|
|
function()
|
|
return InventoryWeight
|
|
end,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
--
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
--
|
|
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[1], 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,
|
|
hasItem =
|
|
function(item, amount, src)
|
|
if src then
|
|
local serverItemCheck = exports[OrigenInv]:getItemCount(src, item, false, false) or 0
|
|
return serverItemCheck >= amount, serverItemCheck
|
|
else
|
|
local localItemCheck = exports[OrigenInv]:Search('count', item) or 0
|
|
return localItemCheck >= amount, localItemCheck
|
|
end
|
|
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,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
--
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
--
|
|
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[1], 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)
|
|
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,
|
|
hasItem =
|
|
function(item, amount, src, invCache)
|
|
local count = 0
|
|
for _, itemData in pairs(invCache) do
|
|
if itemData and itemData.name == item then
|
|
count += (itemData.amount or 0)
|
|
end
|
|
end
|
|
return count >= amount, count
|
|
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()
|
|
return InventoryWeight
|
|
end,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
end,
|
|
getPlayerInv =
|
|
function(src)
|
|
local grabInv = nil
|
|
if src then
|
|
grabInv = exports[CodeMInv]:GetInventory(getPlayer(src).citizenId, src)
|
|
else
|
|
grabInv = exports[CodeMInv]:GetClientPlayerInventory()
|
|
end
|
|
return grabInv
|
|
end,
|
|
invImg =
|
|
function(item)
|
|
return "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "")
|
|
end,
|
|
openShop =
|
|
function(name, label, items)
|
|
TriggerEvent("codem-inventory:openshop", name)
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
--
|
|
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[1], 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,
|
|
hasItem =
|
|
function(item, amount, src)
|
|
if src then
|
|
local serverItemCheck = exports[TgiannInv]:GetItemCount(src, item, nil, true) or 0
|
|
return serverItemCheck >= amount, serverItemCheck
|
|
else
|
|
local localItemCheck = exports[TgiannInv]:GetItemCount(item) or 0
|
|
return localItemCheck >= amount, localItemCheck
|
|
end
|
|
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,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
TriggerServerEvent(getScript()..':server:openServerShop', name)
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
exports[TgiannInv]:OpenShop(source, shopName)
|
|
end,
|
|
registerShop =
|
|
function(name, label, items, society)
|
|
exports[TgiannInv]:RegisterShop(name, items)
|
|
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[1], k)
|
|
exports[TgiannInv]:RemoveItemFromSecondaryInventory("stash", stashName[1], 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)
|
|
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,
|
|
hasItem =
|
|
function(item, amount, src, invCache)
|
|
local count = 0
|
|
for _, itemData in pairs(invCache) do
|
|
if itemData and itemData.name == item then
|
|
count += (itemData.amount or itemData.count or 1)
|
|
end
|
|
end
|
|
return count >= amount, count
|
|
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,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
TriggerServerEvent(getScript()..':server:openServerShop', name)
|
|
TriggerServerEvent("inventory:server:OpenInventory", "shop", label, items)
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
if checkExportExists(JPRInv, "OpenShop") then
|
|
exports[JPRInv]:OpenShop(source, shopName)
|
|
end
|
|
end,
|
|
registerShop =
|
|
function(name, label, items, society)
|
|
exports[JPRInv]:CreateShop({
|
|
name = name,
|
|
label = label,
|
|
slots = #items,
|
|
items = items
|
|
})
|
|
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)
|
|
if checkExportExists(JPRInv, "GetStashItems") then
|
|
return exports[JPRInv]:GetStashItems(stashName) or {}
|
|
else
|
|
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
|
|
return result and json.decode(result) or {}
|
|
end
|
|
end,
|
|
stashAddItem =
|
|
function(stashItems, stashName, items)
|
|
--
|
|
end,
|
|
stashRemoveItem =
|
|
function(stashItems, stashName, items)
|
|
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
|
|
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[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,
|
|
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,
|
|
hasItem =
|
|
function(item, amount, src, invCache)
|
|
local count = 0
|
|
for _, itemData in pairs(invCache) do
|
|
if itemData and itemData.name == item then
|
|
count += (itemData.amount or itemData.count or 1)
|
|
end
|
|
end
|
|
return count >= amount, count
|
|
end,
|
|
canCarry =
|
|
function(itemTable, src)
|
|
local resultTable = {}
|
|
if checkExportExists(QBInv, "CanAddItem") 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,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
TriggerServerEvent(getScript()..':server:openServerShop', name)
|
|
TriggerServerEvent("inventory:server:OpenInventory", "shop", label, items)
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
if checkExportExists(QBInv, "OpenShop") then
|
|
exports[QBInv]:OpenShop(source, shopName)
|
|
end
|
|
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,
|
|
})
|
|
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 checkExportExists(QBInv, "ClearStash") 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 checkExportExists(QBInv, "RemoveItem") then
|
|
for k, v in pairs(items) do
|
|
exports[QBInv]:RemoveItem(stashName[1], 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[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
|
|
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[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
|
|
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,
|
|
hasItem =
|
|
function(item, amount, src, invCache)
|
|
local count = 0
|
|
for _, itemData in pairs(invCache) do
|
|
if itemData and itemData.name == item then
|
|
count += (itemData.amount or itemData.count or 1)
|
|
end
|
|
end
|
|
return count >= amount, count
|
|
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,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
TriggerServerEvent(getScript()..':server:openServerShop', name)
|
|
TriggerServerEvent("inventory:server:OpenInventory", "shop", label, items)
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
if checkExportExists(PSInv, "OpenShop") then
|
|
exports[PSInv]:OpenShop(source, shopName)
|
|
end
|
|
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,
|
|
})
|
|
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[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
|
|
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[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,
|
|
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,
|
|
hasItem =
|
|
function(item, amount, src, invCache)
|
|
local count = 0
|
|
for _, itemData in pairs(invCache) do
|
|
if itemData and itemData.name == item then
|
|
count += (itemData.amount or itemData.count or 1)
|
|
end
|
|
end
|
|
return count >= amount, count
|
|
end,
|
|
canCarry =
|
|
function(itemTable, src)
|
|
local resultTable = {}
|
|
for k, v in pairs(itemTable) do
|
|
resultTable[k] = exports[RSGInv]:CanCarryItem(src, k, v)
|
|
end
|
|
return resultTable
|
|
end,
|
|
getMaxInvWeight =
|
|
function()
|
|
if checkExportExists(RSGInv, "GetMaxWeight") then
|
|
return exports[RSGInv]:GetMaxWeight()
|
|
else
|
|
return InventoryWeight
|
|
end
|
|
end,
|
|
getCurrentInvWeight =
|
|
function(src)
|
|
local weight = 0
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
return weight
|
|
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(name, label, items)
|
|
TriggerServerEvent(getScript()..':server:openServerShop', name)
|
|
end,
|
|
serverOpenShop =
|
|
function(shopName)
|
|
exports[RSGInv]:OpenShop(source, shopName)
|
|
end,
|
|
registerShop =
|
|
function(name, label, items, society)
|
|
exports[RSGInv]:CreateShop({
|
|
name = name,
|
|
label = label,
|
|
slots = #items,
|
|
items = items,
|
|
society = society,
|
|
})
|
|
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[1], 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
|
|
|
|
local hasTable = {}
|
|
for item, amt in pairs(items) do
|
|
if not doesItemExist(item) then
|
|
print("^4ERROR^7: ^2Script can't find ingredient item in Shared Items - ^1"..item.."^7")
|
|
end
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
local hasItem, count = inv.hasItem(item, amt, src, grabInv)
|
|
|
|
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 = hasItem, count = count }
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Fallback to default hasItem function
|
|
if not hasTable or not next(hasTable) then
|
|
for item, amt in pairs(items) do
|
|
local count = 0
|
|
for _, itemData in pairs(grabInv) do
|
|
if itemData and itemData.name == item then
|
|
count += (itemData.amount or itemData.count or 1)
|
|
end
|
|
end
|
|
hasTable[item] = { hasItem = count >= amt, count = count }
|
|
end
|
|
end
|
|
|
|
for _, v in pairs(hasTable) do
|
|
if not v.hasItem then
|
|
return false, hasTable
|
|
end
|
|
end
|
|
return true, hasTable
|
|
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
|
|
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
grabInv = inv.getPlayerInv(src)
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Fallback to framework functions
|
|
if not grabInv then
|
|
if isStarted(QBExport) then
|
|
if src then
|
|
grabInv = Core.Functions.GetPlayer(src).PlayerData.items
|
|
else
|
|
grabInv = Core.Functions.GetPlayerData().items
|
|
end
|
|
elseif isStarted(ESXExport) then
|
|
if src then
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
grabInv = xPlayer and xPlayer.getInventory() or {}
|
|
else
|
|
local xPlayer = ESX.GetPlayerData()
|
|
grabInv = xPlayer and xPlayer.inventory or {}
|
|
end
|
|
end
|
|
end
|
|
|
|
if grabInv == nil 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 i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
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 useableFunc = {
|
|
{ framework = ESXExport,
|
|
func = function(item, funct)
|
|
while not ESX do Wait(0) end
|
|
ESX.RegisterUsableItem(item, funct)
|
|
end,
|
|
},
|
|
{ framework = QBXExport,
|
|
func = function(item, funct)
|
|
exports[QBXExport]:CreateUseableItem(item, funct)
|
|
end,
|
|
},
|
|
{ framework = QBExport,
|
|
func = function(item, funct)
|
|
Core.Functions.CreateUseableItem(item, funct)
|
|
end,
|
|
},
|
|
{ framework = RSGExport,
|
|
func = function(item, funct)
|
|
Core.Functions.CreateUseableItem(item, funct)
|
|
end,
|
|
},
|
|
}
|
|
|
|
for i = 1, #useableFunc do
|
|
local framework = useableFunc[i]
|
|
if isStarted(framework.framework) then
|
|
debugPrint("^6Bridge^7: ^2Registering ^3UsableItem^2 with ^4"..framework.framework.."^7:", item)
|
|
framework.func(item, funct)
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Fallback to framework functiosn
|
|
if isStarted(QBExport) then
|
|
debugPrint("^6Bridge^7: ^2Registering ^3UsableItem^2 with ^4"..QBExport.."^7:", item)
|
|
Core.Functions.CreateUseableItem(item, funct)
|
|
elseif isStarted(ESXExport) then
|
|
debugPrint("^6Bridge^7: ^2Registering ^3UsableItem^2 with ^4"..ESXExport.."^7:", item)
|
|
while not ESX do Wait(0) end
|
|
ESX.RegisterUsableItem(item, funct)
|
|
end
|
|
|
|
debugPrint("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
|
|
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 doesItemExist(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 doesItemExist(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
|
|
|
|
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 i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
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[^6"..invName.."^7] Player(^3"..src.."^7) "..getItemLabel(item).."("..item..") x"..(amount or 1))
|
|
end
|
|
end
|
|
else
|
|
local amountToAdd = amount or 1
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
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[^6"..invName.."^7] Player(^3"..src.."^7) "..getItemLabel(item).."("..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 i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.setItemMetadata(data, src)
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Fallback functions
|
|
if isStarted(QBExport) or isStarted(QBXExport) then -- if qbcore or qbxcore, just use core functions
|
|
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)
|
|
|
|
elseif ESX and isStarted(ESXExport) then -- if esx then use core functions
|
|
--?
|
|
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, src)
|
|
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
|
|
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, nil, src)
|
|
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 i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
return inv.canCarry(itemTable, src)
|
|
end
|
|
end
|
|
|
|
-- Fallback to default canCarry function
|
|
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
|
|
|
|
-------------------------------------------------------------
|
|
-- 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
|
|
|
|
--- Attempts to return the number value of the max inventory weight
|
|
---
|
|
--- @param src number (optional) can be run in server or client.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- local maxInvWeight = getMaxInvWeight()
|
|
--- print("Max Inventory Weight:", maxInvWeight)
|
|
--- ```
|
|
function getMaxInvWeight()
|
|
local weight = 0
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
weight = inv.getMaxInvWeight()
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Fallback function
|
|
if weight == 0 then
|
|
weight = InventoryWeight
|
|
end
|
|
return weight
|
|
end
|
|
|
|
--- Returns current number value of the players inventory weight
|
|
---
|
|
--- @param src number (optional) can be run in server or client.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- local currentInvWeight = getCurrentInvWeight()
|
|
--- print("Current Player Inventory Weight:", currentInvWeight)
|
|
--- ```
|
|
function getCurrentInvWeight(src)
|
|
local weight = 0
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
weight = inv.getCurrentInvWeight(src)
|
|
break
|
|
end
|
|
end
|
|
|
|
-- fallback function
|
|
if weight == 0 then
|
|
local itemcheck = getPlayerInv(src)
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * v.amount) or 0)
|
|
end
|
|
end
|
|
return weight
|
|
end
|
|
|
|
--- Retruns if the player has enough inventory slots free
|
|
---
|
|
--- @param slots number The number of free slots.
|
|
--- @param src number (optional) can be run in server or client.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- local slots = 5
|
|
--- if hasFreeInventorySlots(slots, src) then
|
|
--- print("Player has enough slots free!")
|
|
--- 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)
|
|
local item = type(item) == "string" and item or tostring(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
|
|
|
|
--- Attempts to return the item "label"
|
|
--- If can't be found returns "item (Missing item error)"
|
|
---
|
|
--- @param item string The item name.
|
|
---
|
|
--- @usage
|
|
--- ```lua
|
|
--- local label = getItemLabel("apple")
|
|
--- print("label")
|
|
--- ```
|
|
function getItemLabel(item)
|
|
local item = type(item) == "string" and item or tostring(item)
|
|
|
|
if not item or item == "" then
|
|
return ""
|
|
end
|
|
if not Items or not next(Items) then
|
|
return item.." (Missing item error)"
|
|
end
|
|
if Items[item] ~= nil then
|
|
return Items[item].label
|
|
end
|
|
return item.." (Missing item error)"
|
|
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
|
|
-------------------------------------------------------------
|
|
---
|
|
local stashExploitCheck = {}
|
|
|
|
if isServer() then
|
|
createCallback(getScript()..":getRegisteredStashLocations", function(src, stashName)
|
|
return stashExploitCheck[stashName]
|
|
end)
|
|
end
|
|
|
|
--- 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
|
|
|
|
local exploitCheck = triggerCallback(getScript()..":getRegisteredStashLocations", data.stash)
|
|
|
|
if not exploitCheck then
|
|
print("^3Warning^7: ^2This isn't a registered stash^1, refusing call^7")
|
|
return
|
|
end
|
|
if not distExploitCheck(exploitCheck) then
|
|
return
|
|
end
|
|
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
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 not stashExploitCheck[data.stashName] then
|
|
print("^3Warning^7: ^1Source^7: ^3"..src.." ^1Tried to open a shop^7: ^2This isn't a registered stash^1, refusing call^7")
|
|
return
|
|
end
|
|
if not distExploitCheck(stashExploitCheck[data.stashName], src) then
|
|
return
|
|
end
|
|
|
|
if isStarted(TgiannInv) then
|
|
exports[TgiannInv]:OpenInventory(source, 'stash', data.stashName, data)
|
|
|
|
elseif isStarted(JPRInv) then
|
|
exports[JPRInv]:OpenInventory(source, data.stashName, data)
|
|
|
|
elseif isStarted(QBInv) then
|
|
exports[QBInv]:OpenInventory(source, data.stashName, data)
|
|
|
|
elseif isStarted(PSInv) then
|
|
exports[PSInv]:OpenInventory(source, data.stashName, data)
|
|
|
|
elseif isStarted(RSGInv) then
|
|
exports[RSGInv]:OpenInventory(source, data.stashName, data)
|
|
end
|
|
end)
|
|
|
|
function clearStash(stashId)
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
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)
|
|
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 i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
debugPrint("^6Bridge^7: ^2Retrieving ^3"..inv.invName.." ^2Stash^7:", stashName)
|
|
stashItems = inv.getStash(stashName)
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Fallback to sql checks if no supported inventory found
|
|
if not stashItems or not next(stashItems) then
|
|
if isStarted(QBExport) or isStarted(QBXExport) then
|
|
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
|
|
if result then
|
|
stashItems = json.decode(result)
|
|
end
|
|
elseif ESX and isStarted(ESXExport) then -- if esx then use core functions
|
|
--?
|
|
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 i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.stashRemoveItem(stashItems, stashName, items)
|
|
return
|
|
end
|
|
end
|
|
if isStarted(QBExport) then
|
|
local stashItems = getStash(stashName[1])
|
|
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 ^3QBCORE^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(ESXExport) then
|
|
--?
|
|
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, 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.
|
|
--- Not all
|
|
---
|
|
--- @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)
|
|
|
|
-- ensure tables exist
|
|
stashExploitCheck = stashExploitCheck or {}
|
|
stashExploitCheck[name] = stashExploitCheck[name] or {}
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.registerStash(name, label, slots, weight, owner, coords)
|
|
debugPrint("^6Bridge^7: ^2Registering ^4"..inv.invName.." ^3Stash^7:", name, "^4Label^7: "..label, coords and "- ^4Coord^7: "..formatCoord(coords) or "NO COORD SET")
|
|
break
|
|
end
|
|
end
|
|
|
|
if coords then
|
|
stashExploitCheck[name][#stashExploitCheck[name]+1] = coords.xyz
|
|
else
|
|
print("^3Warning^7: ^1Stash ^4"..name.." ^1doesn^7'^1t have coords^7, ^1this is exploitable^7, ^1add coords to ^3regiterStash^7()")
|
|
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)
|
|
local Ped = PlayerPedId()
|
|
local id = data.metadata and data.metadata.id or data.info and data.info.id or ""
|
|
|
|
if isStarted(OXInv) then
|
|
-- this SHOLD be handled within ox_inventory if set up right
|
|
return
|
|
end
|
|
|
|
openStash({
|
|
stash = id,
|
|
coords = GetEntityCoords(Ped)
|
|
})
|
|
end)
|
|
|
|
|
|
-------------------------------------------------------------
|
|
-- Selling Menu and Animation
|
|
-------------------------------------------------------------
|
|
|
|
local sellShopExploitCheck = {}
|
|
if isServer() then
|
|
createCallback(getScript()..":getRegisteredSellShopLocation", function(src, name)
|
|
return sellShopExploitCheck[name] or nil
|
|
end)
|
|
end
|
|
|
|
--- 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 exploitCheck = triggerCallback(getScript()..":getRegisteredSellShopLocation", data.name)
|
|
if not exploitCheck then
|
|
print("^3Warning^7: ^2This isn't a registered store^1, refusing call^7")
|
|
return
|
|
end
|
|
if not distExploitCheck(exploitCheck) then
|
|
return
|
|
end
|
|
|
|
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 = getItemLabel(k)..(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()
|
|
|
|
local token = triggerCallback(AuthEvent)
|
|
sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end }, token)
|
|
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]
|
|
v.name = data.name
|
|
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, token)
|
|
if not hasItem(data.item, 1) then
|
|
triggerNotify(nil, (Loc[Config.Lan].error["dont_have"] or "You don't have any") .." "..getItemLabel(data.item), "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, token)
|
|
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
|
|
|
|
local bannedItems = {
|
|
money = true,
|
|
cash = true,
|
|
bank = true,
|
|
crypto = true,
|
|
blackmoney = true,
|
|
dirty_money = true,
|
|
markedbills = true
|
|
}
|
|
|
|
--- 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, token)
|
|
local src = source
|
|
|
|
if not checkToken(src, token, "item", "cash") then
|
|
return
|
|
end
|
|
-- Check bannedItems list
|
|
for k, v in pairs(bannedItems) do
|
|
if data.item == k then
|
|
dupeWarn(src, data.item, "^3Source^7: "..src.." ^1Tried to spawn banned item with sell items function")
|
|
return
|
|
end
|
|
end
|
|
|
|
local hasItems, hasTable = hasItem(data.item, 1, src)
|
|
if hasItems then
|
|
removeItem(data.item, hasTable[data.item].count, src)
|
|
print((hasTable[data.item].count * data.price), data.price)
|
|
fundPlayer((hasTable[data.item].count * data.price), "cash", src)
|
|
else
|
|
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..getItemLabel(data.item), "error", src)
|
|
end
|
|
end)
|
|
|
|
function registerSellShop(name, coords)
|
|
if coords then
|
|
debugPrint("^6Bridge^7: ^2Registering ^3Sell Store^7: "..name, coords and "- ^4Coord^7: "..formatCoord(coords) or "NO COORD SET")
|
|
sellShopExploitCheck[name] = sellShopExploitCheck[name] or {}
|
|
sellShopExploitCheck[name][#sellShopExploitCheck[name]+1] = coords
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- Shop Interface
|
|
-------------------------------------------------------------
|
|
|
|
-- Keep a local cache of all registered shops coords, if they are actually registering in the inventory or not
|
|
local shopExploitCheck = {}
|
|
|
|
if isServer() then
|
|
createCallback(getScript()..":getRegisteredShopLocation", function(src, name)
|
|
return shopExploitCheck[name] or nil
|
|
end)
|
|
end
|
|
|
|
--- 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 shop has registered coords, limit players from being too far away from it when opening
|
|
local exploitCheck = triggerCallback(getScript()..":getRegisteredShopLocation", data.shop)
|
|
if not exploitCheck then
|
|
print("^3Warning^7: ^2This isn't a registered store^1, refusing call^7")
|
|
return
|
|
end
|
|
if not distExploitCheck(exploitCheck) then
|
|
return
|
|
end
|
|
|
|
if Config.General.JimShops then
|
|
TriggerServerEvent("jim-shops:ShopOpen", "shop", data.items.label, data.items)
|
|
lookEnt(data.coords)
|
|
return
|
|
end
|
|
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.openShop(data.shop, data.items.label, data.items)
|
|
lookEnt(data.coords)
|
|
return
|
|
end
|
|
end
|
|
|
|
if isStarted(QBExport) then
|
|
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
|
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
|
elseif isStarted(ESXExport) then
|
|
--?
|
|
end
|
|
|
|
end
|
|
|
|
RegisterNetEvent(getScript()..':server:openServerShop', function(shopName)
|
|
local src = source
|
|
|
|
-- If shop has registered coords, limit players from being too far away from it when opening
|
|
if not shopExploitCheck[shopName] then
|
|
print("^3Warning^7: ^1Source^7: ^3"..src.." ^1Tried to open a shop^7: ^2This isn't a registered store^1, refusing call^7")
|
|
return
|
|
end
|
|
if not distExploitCheck(shopExploitCheck[shopName], src) then
|
|
return
|
|
end
|
|
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.serverOpenShop(shopName)
|
|
break
|
|
end
|
|
end
|
|
|
|
end)
|
|
|
|
--- Registers a shop with the active inventory system.
|
|
---
|
|
--- @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, coords)
|
|
-- ensure tables exist
|
|
shopExploitCheck = shopExploitCheck or {}
|
|
shopExploitCheck[name] = shopExploitCheck[name] or {}
|
|
|
|
if isStarted("jim-shops") then
|
|
-- Add support for new jim-shops registration export
|
|
if checkExportExists("jim-shops", "registerShop") then
|
|
exports["jim-shops"]:registerShop(name, label, items, society, coords)
|
|
end
|
|
end
|
|
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.registerShop(name, label, items, society)
|
|
debugPrint("^6Bridge^7: ^2Registering ^5"..inv.invName.." ^3Store^7:", name, "^4Label^7: "..label, coords and "- ^4Coord^7: "..formatCoord(coords) or "NO COORD SET")
|
|
break
|
|
end
|
|
end
|
|
|
|
-- If received coords, pass them to distance check cache
|
|
if coords then
|
|
shopExploitCheck[name][#shopExploitCheck[name]+1] = coords
|
|
else
|
|
print("^3Warning^7: ^1Store ^4"..name.." ^1doesn^7'^1t have coords^7, ^1this is exploitable^7, ^1add coords to ^3regiterShop^7()")
|
|
end
|
|
end
|