local QBCore = exports['qb-core']:GetCoreObject() local vendingMachines = {} local robberyInProgress = {} -- Load vending machines from database CreateThread(function() local result = MySQL.Sync.fetchAll('SELECT * FROM vending_machines') if result then for i = 1, #result do local data = result[i] vendingMachines[data.id] = { id = data.id, owner = data.owner, coords = json.decode(data.coords), prop = data.prop, money = data.money, items = json.decode(data.items) or {}, prices = json.decode(data.prices) or {}, managers = json.decode(data.managers) or {}, stash = 'vending_' .. data.id } end print("^2[VENDING]^7 Loaded " .. #result .. " vending machines") end end) -- Register vending machine (when player buys it) RegisterNetEvent('vending:server:registerMachine', function(coords, prop) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end -- Check if there's already a machine at these coords for id, machine in pairs(vendingMachines) do local dist = #(vector3(coords.x, coords.y, coords.z) - vector3(machine.coords.x, machine.coords.y, machine.coords.z)) if dist < 2.0 then TriggerClientEvent('QBCore:Notify', src, 'Hier ist bereits ein Automat registriert!', 'error') return end end -- Check if player has enough money if Player.PlayerData.money.cash < Config.VendingMachinePrice then TriggerClientEvent('QBCore:Notify', src, 'Du benötigst $' .. Config.VendingMachinePrice .. ' um diesen Automaten zu kaufen!', 'error') return end -- Remove money Player.Functions.RemoveMoney('cash', Config.VendingMachinePrice) -- Create machine in database local machineId = MySQL.insert.await('INSERT INTO vending_machines (owner, coords, prop, money, items, prices, managers) VALUES (?, ?, ?, ?, ?, ?, ?)', { Player.PlayerData.citizenid, json.encode(coords), prop, 0, json.encode({}), json.encode({}), json.encode({}) }) -- Add to memory vendingMachines[machineId] = { id = machineId, owner = Player.PlayerData.citizenid, coords = coords, prop = prop, money = 0, items = {}, prices = {}, managers = {}, stash = 'vending_' .. machineId } print("^2[VENDING]^7 New vending machine registered: " .. machineId) TriggerClientEvent('QBCore:Notify', src, 'Verkaufsautomat erfolgreich gekauft für $' .. Config.VendingMachinePrice .. '!', 'success') TriggerClientEvent('vending:client:refreshTargets', -1) end) -- Sell vending machine RegisterNetEvent('vending:server:sellMachine', function(coords, machineId) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end if not machineId then machineId = getMachineIdByCoords(coords) end if not machineId then TriggerClientEvent('QBCore:Notify', src, 'Automat nicht gefunden!', 'error') return end local machine = vendingMachines[machineId] -- Check if player is owner if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Du bist nicht der Besitzer dieses Automaten!', 'error') return end -- Calculate sell price local sellPrice = math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) -- Add money from machine to sell price sellPrice = sellPrice + machine.money -- Give money to player Player.Functions.AddMoney('cash', sellPrice) -- Empty stash first local stashItems = exports["tgiann-inventory"]:GetSecondaryInventoryItems("stash", machine.stash) if stashItems then for slot, item in pairs(stashItems) do if item.amount > 0 then -- Try to add to player inventory first if Player.Functions.AddItem(item.name, item.amount) then exports["tgiann-inventory"]:RemoveItemFromSecondaryInventory("stash", machine.stash, item.name, item.amount, slot) TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items[item.name], 'add', item.amount) else -- If player inventory is full, create a drop exports["tgiann-inventory"]:RemoveItemFromSecondaryInventory("stash", machine.stash, item.name, item.amount, slot) TriggerClientEvent('QBCore:Notify', src, 'Einige Items wurden auf den Boden fallen gelassen!', 'info') -- If you have a drop system, you can create a drop here end end end end -- Delete from database MySQL.Async.execute('DELETE FROM vending_machines WHERE id = ?', {machineId}) -- Remove from memory vendingMachines[machineId] = nil TriggerClientEvent('QBCore:Notify', src, 'Automat verkauft für $' .. sellPrice .. '!', 'success') TriggerClientEvent('vending:client:refreshTargets', -1) end) -- Check if player can manage machine function canManageMachine(playerId, machineId) local Player = QBCore.Functions.GetPlayer(playerId) if not Player then return false end local machine = vendingMachines[machineId] if not machine then return false end -- Check if player is owner if machine.owner == Player.PlayerData.citizenid then return true end -- Check if player is manager if machine.managers then for _, manager in pairs(machine.managers) do if manager == Player.PlayerData.citizenid then return true end end end return false end -- Open management menu RegisterNetEvent('vending:server:openManagement', function(coords) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end local machine = vendingMachines[machineId] -- Check if player can manage if not canManageMachine(src, machineId) then TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') return end -- Add isOwner flag to distinguish between owner and manager machine.isOwner = (machine.owner == Player.PlayerData.citizenid) TriggerClientEvent('vending:client:openManagement', src, machine) end) -- Open stash RegisterNetEvent('vending:server:openStash', function(coords) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end -- Check if player can manage if not canManageMachine(src, machineId) then TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') return end local machine = vendingMachines[machineId] -- Öffne das Inventar mit tgiann-inventory exports["tgiann-inventory"]:OpenInventory(src, "stash", machine.stash, { maxweight = Config.MaxWeight, slots = Config.MaxSlots, label = 'Vending Machine #' .. machine.id }) end) -- Set item price RegisterNetEvent('vending:server:setItemPrice', function(coords, itemName, price) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end -- Check if player can manage if not canManageMachine(src, machineId) then TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') return end local machine = vendingMachines[machineId] -- Update price machine.prices[itemName] = price MySQL.update('UPDATE vending_machines SET prices = ? WHERE id = ?', {json.encode(machine.prices), machineId}) TriggerClientEvent('QBCore:Notify', src, 'Preis für ' .. (QBCore.Shared.Items[itemName] and QBCore.Shared.Items[itemName].label or itemName) .. ' auf $' .. price .. ' gesetzt!', 'success') end) -- Withdraw money RegisterNetEvent('vending:server:withdrawMoney', function(coords, amount) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end -- Check if player can manage if not canManageMachine(src, machineId) then TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') return end local machine = vendingMachines[machineId] if machine.money < amount then TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Geld im Automaten!', 'error') return end -- Update machine money machine.money = machine.money - amount MySQL.update('UPDATE vending_machines SET money = ? WHERE id = ?', {machine.money, machineId}) -- Give money to player Player.Functions.AddMoney('cash', amount) TriggerClientEvent('QBCore:Notify', src, 'Du hast $' .. amount .. ' abgehoben!', 'success') end) -- Buy item from vending machine with quantity selection RegisterNetEvent('vending:server:buyItem', function(coords, itemName, amount) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end local machine = vendingMachines[machineId] local price = machine.prices[itemName] or Config.DefaultPrice local totalPrice = price * amount -- Check if player has enough money if Player.PlayerData.money.cash < totalPrice then TriggerClientEvent('QBCore:Notify', src, 'Du hast nicht genug Geld!', 'error') return end -- Get stash items local stashItems = exports["tgiann-inventory"]:GetSecondaryInventoryItems("stash", machine.stash) local availableAmount = 0 if stashItems then for slot, item in pairs(stashItems) do if item.name == itemName and item.amount > 0 then availableAmount = availableAmount + item.amount end end end if availableAmount < amount then TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Artikel verfügbar! Verfügbar: ' .. availableAmount, 'error') return end -- Check if player can carry the items if not Player.Functions.AddItem(itemName, amount) then TriggerClientEvent('QBCore:Notify', src, 'Du kannst nicht so viele Items tragen!', 'error') return end -- Remove money from player Player.Functions.RemoveMoney('cash', totalPrice) -- Add money to machine machine.money = machine.money + totalPrice MySQL.update('UPDATE vending_machines SET money = ? WHERE id = ?', {machine.money, machineId}) -- Remove items from stash local remainingToRemove = amount for slot, item in pairs(stashItems) do if item.name == itemName and item.amount > 0 then local removeAmount = math.min(remainingToRemove, item.amount) exports["tgiann-inventory"]:RemoveItemFromSecondaryInventory("stash", machine.stash, itemName, removeAmount, slot) remainingToRemove = remainingToRemove - removeAmount if remainingToRemove <= 0 then break end end end -- Show item box TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items[itemName], 'add', amount) TriggerClientEvent('QBCore:Notify', src, amount .. 'x ' .. (QBCore.Shared.Items[itemName] and QBCore.Shared.Items[itemName].label or itemName) .. ' gekauft für $' .. totalPrice .. '!', 'success') end) -- Add manager to vending machine RegisterNetEvent('vending:server:addManager', function(coords, targetId) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end local machine = vendingMachines[machineId] -- Only owner can add managers if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Nur der Besitzer kann Verwalter hinzufügen!', 'error') return end -- Get target player local Target = QBCore.Functions.GetPlayer(tonumber(targetId)) if not Target then TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error') return end -- Check if already a manager if machine.managers then for _, manager in pairs(machine.managers) do if manager == Target.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Diese Person ist bereits ein Verwalter!', 'error') return end end else machine.managers = {} end -- Add to managers table.insert(machine.managers, Target.PlayerData.citizenid) MySQL.update('UPDATE vending_machines SET managers = ? WHERE id = ?', {json.encode(machine.managers), machineId}) TriggerClientEvent('QBCore:Notify', src, Target.PlayerData.charinfo.firstname .. ' ' .. Target.PlayerData.charinfo.lastname .. ' als Verwalter hinzugefügt!', 'success') TriggerClientEvent('QBCore:Notify', Target.PlayerData.source, 'Du wurdest als Verwalter für einen Verkaufsautomaten hinzugefügt!', 'success') end) -- Remove manager from vending machine RegisterNetEvent('vending:server:removeManager', function(coords, citizenid) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end local machine = vendingMachines[machineId] -- Only owner can remove managers if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Nur der Besitzer kann Verwalter entfernen!', 'error') return end -- Check if manager exists local found = false local newManagers = {} if machine.managers then for _, manager in pairs(machine.managers) do if manager ~= citizenid then table.insert(newManagers, manager) else found = true end end end if not found then TriggerClientEvent('QBCore:Notify', src, 'Diese Person ist kein Verwalter!', 'error') return end -- Update managers machine.managers = newManagers MySQL.update('UPDATE vending_machines SET managers = ? WHERE id = ?', {json.encode(machine.managers), machineId}) TriggerClientEvent('QBCore:Notify', src, 'Verwalter entfernt!', 'success') -- Notify the removed manager if online local players = QBCore.Functions.GetPlayers() for _, playerId in ipairs(players) do local targetPlayer = QBCore.Functions.GetPlayer(playerId) if targetPlayer and targetPlayer.PlayerData.citizenid == citizenid then TriggerClientEvent('QBCore:Notify', targetPlayer.PlayerData.source, 'Du wurdest als Verwalter eines Verkaufsautomaten entfernt!', 'error') break end end end) -- Get managers list QBCore.Functions.CreateCallback('vending:server:getManagers', function(source, cb, coords) local machineId = getMachineIdByCoords(coords) if not machineId then cb({}) return end local machine = vendingMachines[machineId] local managersList = {} if machine.managers and #machine.managers > 0 then for _, citizenid in pairs(machine.managers) do -- Try to get online player info local found = false local players = QBCore.Functions.GetPlayers() for _, playerId in ipairs(players) do local targetPlayer = QBCore.Functions.GetPlayer(playerId) if targetPlayer and targetPlayer.PlayerData.citizenid == citizenid then table.insert(managersList, { citizenid = citizenid, name = targetPlayer.PlayerData.charinfo.firstname .. ' ' .. targetPlayer.PlayerData.charinfo.lastname, online = true }) found = true break end end -- If not online, get from database if not found then local result = MySQL.Sync.fetchAll('SELECT charinfo FROM players WHERE citizenid = ?', {citizenid}) if result and result[1] then local charinfo = json.decode(result[1].charinfo) table.insert(managersList, { citizenid = citizenid, name = charinfo.firstname .. ' ' .. charinfo.lastname, online = false }) else table.insert(managersList, { citizenid = citizenid, name = "Unbekannt", online = false }) end end end end cb(managersList) end) -- Start robbery RegisterNetEvent('vending:server:startRobbery', function(coords) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end local machine = vendingMachines[machineId] -- Check if player has required item local hasItem = Player.Functions.GetItemByName(Config.RobberyItem) if not hasItem or hasItem.amount < 1 then TriggerClientEvent('QBCore:Notify', src, 'Du benötigst einen ' .. Config.RobberyItem, 'error') return end -- Check if already being robbed if robberyInProgress[machineId] then TriggerClientEvent('QBCore:Notify', src, 'Dieser Automat wird bereits aufgebrochen!', 'error') return end -- Check if machine has money if machine.money < Config.MinRobberyAmount then TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Geld im Automaten!', 'error') return end robberyInProgress[machineId] = true -- Alert police local streetHash = GetStreetNameAtCoord(coords.x, coords.y, coords.z) local streetName = GetStreetNameFromHashKey(streetHash) local players = QBCore.Functions.GetQBPlayers() for k, v in pairs(players) do if v.PlayerData.job.name == 'police' and v.PlayerData.job.onduty then TriggerClientEvent('vending:client:policeAlert', v.PlayerData.source, coords, streetName) end end -- Alert owner and managers for _, playerId in ipairs(QBCore.Functions.GetPlayers()) do local targetPlayer = QBCore.Functions.GetPlayer(playerId) if targetPlayer then if targetPlayer.PlayerData.citizenid == machine.owner then TriggerClientEvent('QBCore:Notify', targetPlayer.PlayerData.source, 'Dein Verkaufsautomat wird gerade aufgebrochen! Standort: ' .. streetName, 'error', 10000) elseif machine.managers then for _, manager in pairs(machine.managers) do if targetPlayer.PlayerData.citizenid == manager then TriggerClientEvent('QBCore:Notify', targetPlayer.PlayerData.source, 'Ein Verkaufsautomat, den du verwaltest, wird gerade aufgebrochen! Standort: ' .. streetName, 'error', 10000) break end end end end end TriggerClientEvent('vending:client:startRobbery', src, coords) end) -- Complete robbery RegisterNetEvent('vending:server:completeRobbery', function(coords, success) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end local machineId = getMachineIdByCoords(coords) if not machineId then return end local machine = vendingMachines[machineId] robberyInProgress[machineId] = false if success then local stolenAmount = math.random(Config.MinRobberyAmount, math.min(machine.money, Config.MaxRobberyAmount)) -- Remove money from machine machine.money = machine.money - stolenAmount MySQL.update('UPDATE vending_machines SET money = ? WHERE id = ?', {machine.money, machineId}) -- Give money to player Player.Functions.AddMoney('cash', stolenAmount) TriggerClientEvent('QBCore:Notify', src, 'Du hast $' .. stolenAmount .. ' gestohlen!', 'success') -- Remove robbery item with chance if math.random(1, 100) <= Config.RobberyItemBreakChance then Player.Functions.RemoveItem(Config.RobberyItem, 1) TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items[Config.RobberyItem], 'remove') TriggerClientEvent('QBCore:Notify', src, 'Dein ' .. Config.RobberyItem .. ' ist kaputt gegangen!', 'error') end else TriggerClientEvent('QBCore:Notify', src, 'Aufbruch fehlgeschlagen!', 'error') end end) -- Helper function to get machine ID by coordinates function getMachineIdByCoords(coords) for id, machine in pairs(vendingMachines) do local dist = #(vector3(coords.x, coords.y, coords.z) - vector3(machine.coords.x, machine.coords.y, machine.coords.z)) if dist < 2.0 then return id end end return nil end -- Get machine data by coordinates QBCore.Functions.CreateCallback('vending:server:getMachineByCoords', function(source, cb, coords) local machineId = getMachineIdByCoords(coords) if machineId then cb(vendingMachines[machineId]) else cb(nil) end end) -- Get stash items for vending machine menu QBCore.Functions.CreateCallback('vending:server:getStashItems', function(source, cb, coords) local machineId = getMachineIdByCoords(coords) if not machineId then cb({}) return end local machine = vendingMachines[machineId] -- Get stash items using correct export local stashItems = exports["tgiann-inventory"]:GetSecondaryInventoryItems("stash", machine.stash) local items = {} if stashItems then for slot, item in pairs(stashItems) do if item.amount > 0 then item.price = machine.prices[item.name] or Config.DefaultPrice table.insert(items, item) end end end cb(items) end) -- Check if player owns machine QBCore.Functions.CreateCallback('vending:server:isOwner', function(source, cb, coords) local Player = QBCore.Functions.GetPlayer(source) if not Player then cb(false) return end local machineId = getMachineIdByCoords(coords) if not machineId then cb(false) return end local machine = vendingMachines[machineId] cb(machine.owner == Player.PlayerData.citizenid) end) -- Check if player can manage machine QBCore.Functions.CreateCallback('vending:server:canManage', function(source, cb, coords) local Player = QBCore.Functions.GetPlayer(source) if not Player then cb(false) return end local machineId = getMachineIdByCoords(coords) if not machineId then cb(false) return end cb(canManageMachine(source, machineId)) end) -- Check if machine exists at coords QBCore.Functions.CreateCallback('vending:server:machineExists', function(source, cb, coords) local machineId = getMachineIdByCoords(coords) cb(machineId ~= nil) end) -- Get online players for manager selection QBCore.Functions.CreateCallback('vending:server:getOnlinePlayers', function(source, cb) local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then cb({}) return end local players = {} local onlinePlayers = QBCore.Functions.GetPlayers() for _, playerId in ipairs(onlinePlayers) do local targetPlayer = QBCore.Functions.GetPlayer(playerId) if targetPlayer and targetPlayer.PlayerData.source ~= src then table.insert(players, { id = targetPlayer.PlayerData.source, name = targetPlayer.PlayerData.charinfo.firstname .. ' ' .. targetPlayer.PlayerData.charinfo.lastname, citizenid = targetPlayer.PlayerData.citizenid }) end end cb(players) end) -- Debug command QBCore.Commands.Add('vendingdebug', 'Debug vending machines (Admin Only)', {}, false, function(source, args) local Player = QBCore.Functions.GetPlayer(source) if Player.PlayerData.permission == "admin" or Player.PlayerData.permission == "god" then local count = 0 for id, machine in pairs(vendingMachines) do count = count + 1 print("^2[VENDING]^7 Machine #" .. id .. " | Owner: " .. machine.owner .. " | Money: $" .. machine.money) end TriggerClientEvent('QBCore:Notify', source, count .. ' Verkaufsautomaten geladen', 'success') else TriggerClientEvent('QBCore:Notify', source, 'Keine Berechtigung!', 'error') end end, 'admin')