From e9c57fa6c93c16bc411ba0805aa0f6471932ad3b Mon Sep 17 00:00:00 2001 From: BlazinFourLife <159277485+BlazinFourLife@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:24:17 -0500 Subject: [PATCH] fixes items getting stuck inside trunks, & stashes converts everything from an object to an array --- main.lua | 451 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 main.lua diff --git a/main.lua b/main.lua new file mode 100644 index 00000000..b15fe941 --- /dev/null +++ b/main.lua @@ -0,0 +1,451 @@ +QBCore = exports['qb-core']:GetCoreObject() +Inventories = {} +Drops = {} +RegisteredShops = {} + +CreateThread(function() + MySQL.query('SELECT * FROM inventories', {}, function(result) + if result and #result > 0 then + for i = 1, #result do + local inventory = result[i] + local cacheKey = inventory.identifier + Inventories[cacheKey] = { + items = json.decode(inventory.items) or {}, + isOpen = false + } + end + print(#result .. ' inventories successfully loaded') + end + end) +end) + +CreateThread(function() + while true do + for k, v in pairs(Drops) do + if v and (v.createdTime + (Config.CleanupDropTime * 60) < os.time()) and not Drops[k].isOpen then + local entity = NetworkGetEntityFromNetworkId(v.entityId) + if DoesEntityExist(entity) then DeleteEntity(entity) end + Drops[k] = nil + end + end + Wait(Config.CleanupDropInterval * 60000) + end +end) + +-- Handlers + +AddEventHandler('playerDropped', function() + for _, inv in pairs(Inventories) do + if inv.isOpen == source then + inv.isOpen = false + end + end +end) + +AddEventHandler('txAdmin:events:serverShuttingDown', function() + for inventory, data in pairs(Inventories) do + if data.isOpen then + MySQL.prepare('INSERT INTO inventories (identifier, items) VALUES (?, ?) ON DUPLICATE KEY UPDATE items = ?', { inventory, json.encode(data.items), json.encode(data.items) }) + end + end +end) + +RegisterNetEvent('QBCore:Server:UpdateObject', function() + if source ~= '' then return end + QBCore = exports['qb-core']:GetCoreObject() +end) + +AddEventHandler('QBCore:Server:PlayerLoaded', function(Player) + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'AddItem', function(item, amount, slot, info, reason) + return AddItem(Player.PlayerData.source, item, amount, slot, info, reason) + end) + + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'RemoveItem', function(item, amount, slot, reason) + return RemoveItem(Player.PlayerData.source, item, amount, slot, reason) + end) + + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'GetItemBySlot', function(slot) + return GetItemBySlot(Player.PlayerData.source, slot) + end) + + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'GetItemByName', function(item) + return GetItemByName(Player.PlayerData.source, item) + end) + + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'GetItemsByName', function(item) + return GetItemsByName(Player.PlayerData.source, item) + end) + + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'ClearInventory', function(filterItems) + ClearInventory(Player.PlayerData.source, filterItems) + end) + + QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, 'SetInventory', function(items) + SetInventory(Player.PlayerData.source, items) + end) +end) + +AddEventHandler('onResourceStart', function(resourceName) + if resourceName ~= GetCurrentResourceName() then return end + local Players = QBCore.Functions.GetQBPlayers() + for k in pairs(Players) do + QBCore.Functions.AddPlayerMethod(k, 'AddItem', function(item, amount, slot, info) + return AddItem(k, item, amount, slot, info) + end) + + QBCore.Functions.AddPlayerMethod(k, 'RemoveItem', function(item, amount, slot) + return RemoveItem(k, item, amount, slot) + end) + + QBCore.Functions.AddPlayerMethod(k, 'GetItemBySlot', function(slot) + return GetItemBySlot(k, slot) + end) + + QBCore.Functions.AddPlayerMethod(k, 'GetItemByName', function(item) + return GetItemByName(k, item) + end) + + QBCore.Functions.AddPlayerMethod(k, 'GetItemsByName', function(item) + return GetItemsByName(k, item) + end) + + QBCore.Functions.AddPlayerMethod(k, 'ClearInventory', function(filterItems) + ClearInventory(k, filterItems) + end) + + QBCore.Functions.AddPlayerMethod(k, 'SetInventory', function(items) + SetInventory(k, items) + end) + end +end) + +-- Functions + +local function checkWeapon(source, item) + local ped = GetPlayerPed(source) + local weapon = GetSelectedPedWeapon(ped) + local weaponInfo = QBCore.Shared.Weapons[weapon] + if weaponInfo and weaponInfo.name == item.name then + RemoveWeaponFromPed(ped, weapon) + end +end + +-- Events + +RegisterNetEvent('qb-inventory:server:openVending', function() + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + CreateShop({ + name = 'vending', + label = 'Vending Machine', + coords = vendingMachineCoords, + slots = #Config.VendingItems, + items = Config.VendingItems + }) + OpenShop(src, 'vending') +end) + +RegisterNetEvent('qb-inventory:server:closeInventory', function(inventory) + local src = source + local QBPlayer = QBCore.Functions.GetPlayer(src) + if not QBPlayer then return end + Player(source).state.inv_busy = false + if inventory:find('shop-') then return end + if inventory:find('otherplayer-') then + local targetId = tonumber(inventory:match('otherplayer%-(.+)')) + Player(targetId).state.inv_busy = false + return + end + if Drops[inventory] then + Drops[inventory].isOpen = false + if #Drops[inventory].items == 0 and not Drops[inventory].isOpen then -- if no listeed items in the drop on close + TriggerClientEvent('qb-inventory:client:removeDropTarget', -1, Drops[inventory].entityId) + Wait(500) + local entity = NetworkGetEntityFromNetworkId(Drops[inventory].entityId) + if DoesEntityExist(entity) then DeleteEntity(entity) end + Drops[inventory] = nil + end + return + end + if not Inventories[inventory] then return end + Inventories[inventory].isOpen = false + MySQL.prepare('INSERT INTO inventories (identifier, items) VALUES (?, ?) ON DUPLICATE KEY UPDATE items = ?', { inventory, json.encode(Inventories[inventory].items), json.encode(Inventories[inventory].items) }) +end) + +RegisterNetEvent('qb-inventory:server:useItem', function(item) + local src = source + local itemData = GetItemBySlot(src, item.slot) + if not itemData then return end + local itemInfo = QBCore.Shared.Items[itemData.name] + if itemData.type == 'weapon' then + TriggerClientEvent('qb-weapons:client:UseWeapon', src, itemData, itemData.info.quality and itemData.info.quality > 0) + TriggerClientEvent('qb-inventory:client:ItemBox', src, itemInfo, 'use') + else + UseItem(itemData.name, src, itemData) + TriggerClientEvent('qb-inventory:client:ItemBox', src, itemInfo, 'use') + end +end) + +RegisterNetEvent('qb-inventory:server:openDrop', function(dropId) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + local playerPed = GetPlayerPed(src) + local playerCoords = GetEntityCoords(playerPed) + local drop = Drops[dropId] + if not drop then return end + if drop.isOpen then return end + local distance = #(playerCoords - drop.coords) + if distance > 2.5 then return end + local formattedInventory = { + name = dropId, + label = dropId, + maxweight = drop.maxweight, + slots = drop.slots, + inventory = drop.items + } + drop.isOpen = true + TriggerClientEvent('qb-inventory:client:openInventory', source, Player.PlayerData.items, formattedInventory) +end) + +RegisterNetEvent('qb-inventory:server:updateDrop', function(dropId, coords) + Drops[dropId].coords = coords +end) + +RegisterNetEvent('qb-inventory:server:snowball', function(action) + if action == 'add' then + AddItem(source, 'weapon_snowball', 1, false, false, 'qb-inventory:server:snowball') + elseif action == 'remove' then + RemoveItem(source, 'weapon_snowball', 1, false, 'qb-inventory:server:snowball') + end +end) + +-- Callbacks + +QBCore.Functions.CreateCallback('qb-inventory:server:GetCurrentDrops', function(_, cb) + cb(Drops) +end) + +QBCore.Functions.CreateCallback('qb-inventory:server:createDrop', function(source, cb, item) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then + cb(false) + return + end + local playerPed = GetPlayerPed(src) + local playerCoords = GetEntityCoords(playerPed) + if RemoveItem(src, item.name, item.amount, item.fromSlot, 'dropped item') then + if item.type == 'weapon' then checkWeapon(src, item) end + TaskPlayAnim(playerPed, 'pickup_object', 'pickup_low', 8.0, -8.0, 2000, 0, 0, false, false, false) + local bag = CreateObjectNoOffset(Config.ItemDropObject, playerCoords.x + 0.5, playerCoords.y + 0.5, playerCoords.z, true, true, false) + local dropId = NetworkGetNetworkIdFromEntity(bag) + local newDropId = 'drop-' .. dropId + if not Drops[newDropId] then + Drops[newDropId] = { + name = newDropId, + label = 'Drop', + items = { item }, + entityId = dropId, + createdTime = os.time(), + coords = playerCoords, + maxweight = Config.DropSize.maxweight, + slots = Config.DropSize.slots, + isOpen = true + } + TriggerClientEvent('qb-inventory:client:setupDropTarget', -1, dropId) + else + table.insert(Drops[newDropId].items, item) + end + cb(dropId) + else + cb(false) + end +end) + +QBCore.Functions.CreateCallback('qb-inventory:server:attemptPurchase', function(source, cb, data) + local itemInfo = data.item + local amount = data.amount + local shop = string.gsub(data.shop, 'shop%-', '') + print(json.encode(data,{indent=true})) + local price = itemInfo.price * amount + local Player = QBCore.Functions.GetPlayer(source) + + if not Player then + cb(false) + return + end + + local shopInfo = RegisteredShops[shop] + if not shopInfo then + cb(false) + return + end + + local playerPed = GetPlayerPed(source) + local playerCoords = GetEntityCoords(playerPed) + if shopInfo.coords then + local shopCoords = vector3(shopInfo.coords.x, shopInfo.coords.y, shopInfo.coords.z) + if #(playerCoords - shopCoords) > 10 then + cb(false) + return + end + end + + if not CanAddItem(source, itemInfo.name, amount) then + TriggerClientEvent('QBCore:Notify', source, 'Cannot hold item', 'error') + cb(false) + return + end + + if Player.PlayerData.money.cash >= price then + Player.Functions.RemoveMoney('cash', price, 'shop-purchase') + AddItem(source, itemInfo.name, amount, nil, itemInfo.info, 'shop-purchase') + TriggerEvent('qb-shops:server:UpdateShopItems', shop, itemInfo, amount) + cb(true) + else + TriggerClientEvent('QBCore:Notify', source, 'You do not have enough money', 'error') + cb(false) + end +end) + +QBCore.Functions.CreateCallback('qb-inventory:server:giveItem', function(source, cb, target, item, amount) + local player = QBCore.Functions.GetPlayer(source) + if not player or player.PlayerData.metadata['isdead'] or player.PlayerData.metadata['inlaststand'] or player.PlayerData.metadata['ishandcuffed'] then + cb(false) + return + end + local playerPed = GetPlayerPed(source) + + local Target = QBCore.Functions.GetPlayer(target) + if not Target or Target.PlayerData.metadata['isdead'] or Target.PlayerData.metadata['inlaststand'] or Target.PlayerData.metadata['ishandcuffed'] then + cb(false) + return + end + local targetPed = GetPlayerPed(target) + + local pCoords = GetEntityCoords(playerPed) + local tCoords = GetEntityCoords(targetPed) + if #(pCoords - tCoords) > 5 then + cb(false) + return + end + + local itemInfo = QBCore.Shared.Items[item:lower()] + if not itemInfo then + cb(false) + return + end + + local hasItem = HasItem(source, item) + if not hasItem then + cb(false) + return + end + + local itemAmount = GetItemByName(source, item).amount + if itemAmount <= 0 then + cb(false) + return + end + + local giveAmount = tonumber(amount) + if giveAmount > itemAmount then + cb(false) + return + end + + local giveItem = AddItem(target, item, giveAmount) + if not giveItem then + cb(false) + return + end + + local removeItem = RemoveItem(source, item, giveAmount) + if not removeItem then + cb(false) + return + end + + if itemInfo.type == 'weapon' then checkWeapon(source, item) end + TriggerClientEvent('qb-inventory:client:giveAnim', source) + TriggerClientEvent('qb-inventory:client:ItemBox', source, itemInfo, 'remove', giveAmount) + TriggerClientEvent('qb-inventory:client:giveAnim', target) + TriggerClientEvent('qb-inventory:client:ItemBox', target, itemInfo, 'add', giveAmount) + if Player(target).state.inv_busy then TriggerClientEvent('qb-inventory:client:updateInventory', target) end + cb(true) +end) + +-- Item move logic + +local function getItem(inventoryId, src, slot) + local item + if inventoryId == 'player' then + local Player = QBCore.Functions.GetPlayer(src) + item = Player.PlayerData.items[slot] + elseif inventoryId:find('otherplayer-') then + local targetId = tonumber(inventoryId:match('otherplayer%-(.+)')) + local targetPlayer = QBCore.Functions.GetPlayer(targetId) + if targetPlayer then + item = targetPlayer.PlayerData.items[slot] + end + elseif inventoryId:find('drop-') then + item = Drops[inventoryId]['items'][slot] + else + item = Inventories[inventoryId]['items'][slot] + end + return item +end + +local function getIdentifier(inventoryId, src) + if inventoryId == 'player' then + return src + elseif inventoryId:find('otherplayer-') then + return tonumber(inventoryId:match('otherplayer%-(.+)')) + else + return inventoryId + end +end + +RegisterNetEvent('qb-inventory:server:SetInventoryData', function(fromInventory, toInventory, fromSlot, toSlot, fromAmount, toAmount) + if toInventory:find('shop-') then return end + if not fromInventory or not toInventory or not fromSlot or not toSlot or not fromAmount or not toAmount then return end + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + + fromSlot, toSlot, fromAmount, toAmount = tonumber(fromSlot), tonumber(toSlot), tonumber(fromAmount), tonumber(toAmount) + + local fromItem = getItem(fromInventory, src, fromSlot) + local toItem = getItem(toInventory, src, toSlot) + + if fromItem then + if not toItem and toAmount > fromItem.amount then return end + if fromInventory == 'player' and toInventory ~= 'player' then checkWeapon(src, fromItem) end + + local fromId = getIdentifier(fromInventory, src) + local toId = getIdentifier(toInventory, src) + + if toItem and fromItem.name == toItem.name then + if RemoveItem(fromId, fromItem.name, toAmount, fromSlot, 'stacked item') then + AddItem(toId, toItem.name, toAmount, toSlot, toItem.info, 'stacked item') + end + elseif not toItem and toAmount < fromAmount then + if RemoveItem(fromId, fromItem.name, toAmount, fromSlot, 'split item') then + AddItem(toId, fromItem.name, toAmount, toSlot, fromItem.info, 'split item') + end + else + if toItem then + if RemoveItem(fromId, fromItem.name, fromAmount, fromSlot, 'swapped item') and RemoveItem(toId, toItem.name, toAmount, toSlot, 'swapped item') then + AddItem(toId, fromItem.name, fromAmount, toSlot, fromItem.info, 'swapped item') + AddItem(fromId, toItem.name, toAmount, fromSlot, toItem.info, 'swapped item') + end + else + if RemoveItem(fromId, fromItem.name, toAmount, fromSlot, 'moved item') then + AddItem(toId, fromItem.name, toAmount, toSlot, fromItem.info, 'moved item') + end + end + end + end +end)