mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-17 05:56:02 +01:00
Add a check for if a person is attempting to trigger remove minus of an item (possibly trying to give them selves items) this will stop it in its tracks and warn the server console what happened
2970 lines
110 KiB
Lua
2970 lines
110 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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
exports[OXInv]:SetMetadata(stash, slot, metadata)
|
|
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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
local itemData = exports[CoreInv]:getItemBySlot(stash, slot) or {}
|
|
if itemData then
|
|
exports[CoreInv]:setItem(stash, itemData.item, itemData.count, metadata)
|
|
end
|
|
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[OrigenInv]:GetInventoryItems(src)
|
|
else
|
|
grabInv = exports[OrigenInv]:GetInventory()
|
|
end
|
|
return grabInv
|
|
end,
|
|
invImg =
|
|
function(item)
|
|
return "nui://"..OrigenInv.."/html/images/"..(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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
exports[OrigenInv]:setMetadata(stash, slot, metadata)
|
|
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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
|
|
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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
|
|
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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
|
|
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,
|
|
stashEditMetadata =
|
|
function(stash, slot, metadata)
|
|
local stashData = getStash(stash)
|
|
for i = 1, #stashData do
|
|
if stashData[i].slot == slot then
|
|
print("found slot")
|
|
stashData[i].info = metadata
|
|
end
|
|
jsonPrint(stashData[i])
|
|
end
|
|
|
|
debugPrint("^6Bridge^7: ^3stashEditMetadata^7: ^2Saving ^3QB^2 stash '^6"..stash.."^7'")
|
|
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
|
['stash'] = stash,
|
|
['items'] = json.encode(stashData)
|
|
})
|
|
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
|
|
if isStarted(ESXExport) then
|
|
for item, amt in pairs(items) do
|
|
local count = 0
|
|
for itemName, amount in pairs(grabInv) do
|
|
if itemName and itemName == item then
|
|
count += (amount or 1)
|
|
end
|
|
end
|
|
hasTable[item] = { hasItem = count >= amt, count = count }
|
|
end
|
|
else
|
|
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
|
|
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
|
|
local foundInv = ""
|
|
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
foundInv = inv.invName
|
|
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
|
|
grabInv = triggerCallback(getScript()..":GetESXInv")
|
|
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
|
|
|
|
if isServer() then
|
|
createCallback(getScript()..":GetESXInv", function(source)
|
|
local src = source
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
local inv = xPlayer.getInventory()
|
|
return xPlayer.getInventory(src)
|
|
end)
|
|
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)
|
|
local excludeRes = {
|
|
[QBExport] = true,
|
|
[ESXExport] = true,
|
|
[VorpExport] = true,
|
|
}
|
|
local invokingRes = GetInvokingResource()
|
|
|
|
--debugPrint(GetInvokingResource())
|
|
if invokingRes and invokingRes ~= getScript() and not excludeRes[invokingRes] then
|
|
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
|
return
|
|
end
|
|
|
|
if not doesItemExist(item) then
|
|
print("^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 remamount < 0 then
|
|
print("^1Error^7: ^7src - ^1Client tried to remove a minus number of item ^7- "..item)
|
|
return
|
|
end
|
|
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)
|
|
if isStarted(ESXExport) then
|
|
for _, amount in pairs(itemcheck) do
|
|
weight += (amount or 0)
|
|
end
|
|
else
|
|
for _, v in pairs(itemcheck) do
|
|
weight += ((v.weight * (v.amount or v.count)) or 0)
|
|
end
|
|
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
|
|
|
|
|
|
function stashEditMetadata(stash, slot, metadata)
|
|
for i = 1, #InvFunc do
|
|
local inv = InvFunc[i]
|
|
if isStarted(inv.invName) then
|
|
inv.stashEditMetadata(stash, slot, metadata)
|
|
return
|
|
end
|
|
end
|
|
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)
|
|
goto skip
|
|
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
|
|
::skip::
|
|
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
|
|
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
|
|
|
|
-- Fallback to core functions
|
|
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)
|
|
|
|
function stashAddItem(stashItems, stashName, items)
|
|
-- wip
|
|
end
|
|
|
|
-------------------------------------------------------------
|
|
-- 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, stashData)
|
|
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),
|
|
maxWeight = stashData and stashData.maxWeight or 100000,
|
|
slots = stashData and stashData.slots or 5,
|
|
})
|
|
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)
|
|
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
|
|
if not data.items.items[1] then
|
|
local shopMenu = {}
|
|
for k, v in pairs(data.items.items) do
|
|
local clonedTable = cloneTable(data)
|
|
local itemsTable = v.items or v.Items
|
|
shopMenu[#shopMenu+1] = {
|
|
header = v.header or k,
|
|
txt = countTable(itemsTable).." Products",
|
|
onSelect = function()
|
|
--clonedTable.originShop = data.shop
|
|
clonedTable.shop = data.shop.."_"..k
|
|
clonedTable.label = data.items.label.." - "..(v.header or k)
|
|
clonedTable.slots = #itemsTable
|
|
clonedTable.items.items = itemsTable
|
|
openShop(clonedTable)
|
|
end,
|
|
}
|
|
end
|
|
return openMenu(shopMenu, { header = data.items.label, canClose = true, })
|
|
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'"..shopName.."' ^2This isn't a registered store^7, ^1refusing 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
|
|
if not items[1] then
|
|
for k, v in pairs(items) do
|
|
inv.registerShop(name.."_"..k, label.." - "..(v.header or k), v.items, society)
|
|
shopExploitCheck[name.."_"..k] = shopExploitCheck[name.."_"..k] or {}
|
|
shopExploitCheck[name.."_"..k][#shopExploitCheck[name.."_"..k]+1] = coords
|
|
|
|
debugPrint("^6Bridge^7: ^2Registering ^5"..inv.invName.." ^3Store^7:", name.."_"..k, "^4Label^7: "..label.." - "..(v.header or k), coords and "- ^4Coord^7: "..formatCoord(coords) or "NO COORD SET")
|
|
end
|
|
else
|
|
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")
|
|
end
|
|
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
|