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 {}, 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) VALUES (?, ?, ?, ?, ?, ?)', { Player.PlayerData.citizenid, json.encode(coords), prop, 0, json.encode({}), json.encode({}) }) -- Add to memory vendingMachines[machineId] = { id = machineId, owner = Player.PlayerData.citizenid, coords = coords, prop = prop, money = 0, items = {}, prices = {}, 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) -- 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] if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Das ist nicht dein Verkaufsautomat!', 'error') return end TriggerClientEvent('vending:client:openManagement', src, machine) end) -- Open stash (korrekt für tgiann-inventory) 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 local machine = vendingMachines[machineId] if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Das ist nicht dein Verkaufsautomat!', 'error') return end -- Ö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 local machine = vendingMachines[machineId] if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Das ist nicht dein Verkaufsautomat!', 'error') return end -- 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 local machine = vendingMachines[machineId] if machine.owner ~= Player.PlayerData.citizenid then TriggerClientEvent('QBCore:Notify', src, 'Das ist nicht dein Verkaufsautomat!', 'error') return end 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 RegisterNetEvent('vending:server:buyItem', function(coords, itemName) 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 -- Check if player has enough money if Player.PlayerData.money.cash < price 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 hasItem = false if stashItems then for slot, item in pairs(stashItems) do if item.name == itemName and item.amount > 0 then hasItem = true break end end end if not hasItem then TriggerClientEvent('QBCore:Notify', src, 'Artikel nicht verfügbar!', 'error') return end -- Remove money from player Player.Functions.RemoveMoney('cash', price) -- Add money to machine machine.money = machine.money + price MySQL.update('UPDATE vending_machines SET money = ? WHERE id = ?', {machine.money, machineId}) -- Remove item from stash and add to player exports["tgiann-inventory"]:RemoveItemFromSecondaryInventory("stash", machine.stash, itemName, 1) Player.Functions.AddItem(itemName, 1) TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items[itemName], 'add') TriggerClientEvent('QBCore:Notify', src, 'Artikel gekauft für $' .. price .. '!', 'success') 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 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 machine exists at coords QBCore.Functions.CreateCallback('vending:server:machineExists', function(source, cb, coords) local machineId = getMachineIdByCoords(coords) cb(machineId ~= nil) 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')