local QBCore = exports['qb-core']:GetCoreObject() local nearbyMachine = nil local currentMachineData = nil local isInteracting = false -- Function to draw 3D text in the world function DrawText3D(x, y, z, text) SetTextScale(0.35, 0.35) SetTextFont(4) SetTextProportional(1) SetTextColour(255, 255, 255, 215) SetTextEntry("STRING") SetTextCentre(1) AddTextComponentString(text) SetDrawOrigin(x, y, z, 0) DrawText(0.0, 0.0) local factor = (string.len(text)) / 370 DrawRect(0.0, 0.0+0.0125, 0.017+ factor, 0.03, 0, 0, 0, 75) ClearDrawOrigin() end -- Function to check if machine is registered function isRegisteredMachine(entity) local coords = GetEntityCoords(entity) local isRegistered = false QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists) isRegistered = exists end, coords) -- Wait for callback local timeout = 0 while isRegistered == false and timeout < 100 do Wait(10) timeout = timeout + 1 end return isRegistered end -- Check if player can manage machine function canManageMachine(entity) local coords = GetEntityCoords(entity) local canManage = false QBCore.Functions.TriggerCallback('vending:server:canManage', function(result) canManage = result end, coords) -- Wait for callback local timeout = 0 while canManage == false and timeout < 100 do Wait(10) timeout = timeout + 1 end return canManage end -- Main thread to detect nearby vending machines CreateThread(function() while true do local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local wait = 1000 local foundMachine = false -- Check for nearby vending machines for _, propName in ipairs(Config.VendingProps) do local hash = GetHashKey(propName) local objects = GetGamePool('CObject') for _, obj in ipairs(objects) do if GetEntityModel(obj) == hash then local objCoords = GetEntityCoords(obj) local dist = #(playerCoords - objCoords) if dist < 2.0 then wait = 0 foundMachine = true nearbyMachine = obj -- Only check status if not already interacting if not isInteracting then local z = objCoords.z + 1.0 local registered = isRegisteredMachine(obj) if registered then local canManage = canManageMachine(obj) if canManage then DrawText3D(objCoords.x, objCoords.y, z, "[E] Kaufen | [G] Verwalten") -- Handle key presses for management if IsControlJustPressed(0, 38) then -- E key isInteracting = true TriggerEvent('vending:client:openBuyMenu', {entity = obj}) Wait(500) -- Prevent multiple triggers isInteracting = false elseif IsControlJustPressed(0, 47) then -- G key isInteracting = true TriggerEvent('vending:client:openOwnerMenu', {entity = obj}) Wait(500) -- Prevent multiple triggers isInteracting = false end else DrawText3D(objCoords.x, objCoords.y, z, "[E] Kaufen | [G] Aufbrechen") -- Handle key presses for buying/robbery if IsControlJustPressed(0, 38) then -- E key isInteracting = true TriggerEvent('vending:client:openBuyMenu', {entity = obj}) Wait(500) -- Prevent multiple triggers isInteracting = false elseif IsControlJustPressed(0, 47) then -- G key isInteracting = true TriggerEvent('vending:client:startRobbery', {entity = obj}) Wait(500) -- Prevent multiple triggers isInteracting = false end end else DrawText3D(objCoords.x, objCoords.y, z, "[E] Automaten kaufen ($" .. Config.VendingMachinePrice .. ")") -- Handle key press for buying machine if IsControlJustPressed(0, 38) then -- E key isInteracting = true TriggerEvent('vending:client:buyMachine', {entity = obj}) Wait(500) -- Prevent multiple triggers isInteracting = false end end end break end end end if foundMachine then break end end if not foundMachine then nearbyMachine = nil end Wait(wait) end end) -- Buy vending machine RegisterNetEvent('vending:client:buyMachine', function(data) local entity = data.entity local coords = GetEntityCoords(entity) local model = GetEntityModel(entity) local prop = nil -- Find prop name for i = 1, #Config.VendingProps do if GetHashKey(Config.VendingProps[i]) == model then prop = Config.VendingProps[i] break end end if not prop then return end lib.registerContext({ id = 'vending_buy_confirm', title = 'Verkaufsautomat kaufen', options = { { title = 'Bestätigen', description = 'Automaten für $' .. Config.VendingMachinePrice .. ' kaufen', icon = 'fas fa-check', onSelect = function() TriggerServerEvent('vending:server:registerMachine', coords, prop) end }, { title = 'Abbrechen', description = 'Kauf abbrechen', icon = 'fas fa-times' } } }) lib.showContext('vending_buy_confirm') end) -- Open buy menu with quantity selection RegisterNetEvent('vending:client:openBuyMenu', function(data) local entity = data.entity local coords = GetEntityCoords(entity) QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items) if #items == 0 then QBCore.Functions.Notify('Dieser Automat ist leer!', 'error') return end local options = {} for i = 1, #items do local item = items[i] if item.amount > 0 then local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name table.insert(options, { title = itemLabel, description = 'Preis: $' .. item.price .. ' | Verfügbar: ' .. item.amount, icon = 'fas fa-shopping-cart', onSelect = function() openQuantityDialog(coords, item.name, item.price, item.amount, itemLabel) end }) end end if #options == 0 then QBCore.Functions.Notify('Keine Artikel verfügbar!', 'error') return end lib.registerContext({ id = 'vending_buy_menu', title = 'Verkaufsautomat', options = options }) lib.showContext('vending_buy_menu') end, coords) end) -- Open quantity dialog for buying items function openQuantityDialog(coords, itemName, price, maxAmount, itemLabel) local input = lib.inputDialog('Menge auswählen', { { type = 'number', label = itemLabel .. ' - $' .. price .. ' pro Stück', description = 'Wie viele möchtest du kaufen? (Max: ' .. maxAmount .. ')', required = true, min = 1, max = maxAmount, default = 1 } }) if input and input[1] then local amount = tonumber(input[1]) if amount > 0 and amount <= maxAmount then TriggerServerEvent('vending:server:buyItem', coords, itemName, amount) else QBCore.Functions.Notify('Ungültige Menge!', 'error') end end end -- Open owner menu RegisterNetEvent('vending:client:openOwnerMenu', function(data) local entity = data.entity local coords = GetEntityCoords(entity) QBCore.Functions.TriggerCallback('vending:server:getMachineByCoords', function(machine) if not machine then QBCore.Functions.Notify('Automat nicht gefunden!', 'error') return end local options = { { title = 'Inventar verwalten', description = 'Items hinzufügen/entfernen', icon = 'fas fa-box', onSelect = function() TriggerServerEvent('vending:server:openStash', coords) end }, { title = 'Preise festlegen', description = 'Verkaufspreise für Items setzen', icon = 'fas fa-tags', onSelect = function() openPriceMenu(coords) end }, { title = 'Geld abheben', description = 'Verfügbar: $' .. machine.money, icon = 'fas fa-money-bill', onSelect = function() openWithdrawMenu(coords, machine.money) end }, { title = 'Statistiken', description = 'Verkaufsstatistiken anzeigen', icon = 'fas fa-chart-bar', onSelect = function() openStatsMenu(machine) end } } -- Add manager options only for owner if machine.isOwner then table.insert(options, { title = 'Verwalter', description = 'Verwalter hinzufügen/entfernen', icon = 'fas fa-users-cog', onSelect = function() openManagersMenu(coords) end }) -- Add sell option only for owner table.insert(options, { title = 'Automaten verkaufen', description = 'Verkaufe den Automaten für ' .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. '$', icon = 'fas fa-dollar-sign', onSelect = function() sellVendingMachine(coords, machine.id) end }) end lib.registerContext({ id = 'vending_owner_menu', title = 'Verkaufsautomat Verwaltung', options = options }) lib.showContext('vending_owner_menu') end, coords) end) -- Function to sell the vending machine function sellVendingMachine(coords, machineId) local input = lib.inputDialog('Automaten verkaufen', { { type = 'checkbox', label = 'Bestätigen', description = 'Du erhältst ' .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. '$ zurück. Diese Aktion kann nicht rückgängig gemacht werden!', required = true } }) if input and input[1] then TriggerServerEvent('vending:server:sellMachine', coords, machineId) end end -- Open price menu function openPriceMenu(coords) QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items) if #items == 0 then QBCore.Functions.Notify('Keine Items im Automaten!', 'error') return end local options = {} for i = 1, #items do local item = items[i] local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name table.insert(options, { title = itemLabel, description = 'Aktueller Preis: $' .. item.price, icon = 'fas fa-tag', onSelect = function() setPriceForItem(coords, item.name, itemLabel) end }) end lib.registerContext({ id = 'vending_price_menu', title = 'Preise festlegen', menu = 'vending_owner_menu', options = options }) lib.showContext('vending_price_menu') end, coords) end -- Set price for specific item function setPriceForItem(coords, itemName, itemLabel) local input = lib.inputDialog('Preis festlegen', { { type = 'number', label = 'Preis für ' .. itemLabel, description = 'Neuen Verkaufspreis eingeben', required = true, min = 1, max = 10000 } }) if input and input[1] then TriggerServerEvent('vending:server:setItemPrice', coords, itemName, tonumber(input[1])) end end -- Open withdraw menu function openWithdrawMenu(coords, availableMoney) if availableMoney <= 0 then QBCore.Functions.Notify('Kein Geld im Automaten!', 'error') return end local input = lib.inputDialog('Geld abheben', { { type = 'number', label = 'Betrag (Verfügbar: $' .. availableMoney .. ')', description = 'Wie viel möchtest du abheben?', required = true, min = 1, max = availableMoney } }) if input and input[1] then TriggerServerEvent('vending:server:withdrawMoney', coords, tonumber(input[1])) end end -- Open stats menu function openStatsMenu(machine) lib.registerContext({ id = 'vending_stats_menu', title = 'Verkaufsstatistiken', menu = 'vending_owner_menu', options = { { title = 'Gesamteinnahmen', description = '$' .. machine.money, icon = 'fas fa-dollar-sign' }, { title = 'Automat ID', description = '#' .. machine.id, icon = 'fas fa-hashtag' }, { title = 'Standort', description = 'X: ' .. math.floor(machine.coords.x) .. ' Y: ' .. math.floor(machine.coords.y), icon = 'fas fa-map-marker-alt' } } }) lib.showContext('vending_stats_menu') end -- Open managers menu function openManagersMenu(coords) -- Get current managers QBCore.Functions.TriggerCallback('vending:server:getManagers', function(managers) local options = { { title = 'Verwalter hinzufügen', description = 'Neuen Verwalter hinzufügen', icon = 'fas fa-user-plus', onSelect = function() openAddManagerMenu(coords) end } } -- Add existing managers with remove option if #managers > 0 then for i = 1, #managers do local manager = managers[i] table.insert(options, { title = manager.name, description = manager.online and 'Online' or 'Offline', icon = manager.online and 'fas fa-circle text-success' or 'fas fa-circle text-danger', onSelect = function() lib.registerContext({ id = 'manager_options', title = 'Verwalter: ' .. manager.name, menu = 'managers_menu', options = { { title = 'Entfernen', description = 'Verwalter entfernen', icon = 'fas fa-user-minus', onSelect = function() TriggerServerEvent('vending:server:removeManager', coords, manager.citizenid) Wait(500) openManagersMenu(coords) -- Refresh the menu end } } }) lib.showContext('manager_options') end }) end else table.insert(options, { title = 'Keine Verwalter', description = 'Es sind keine Verwalter vorhanden', icon = 'fas fa-info-circle', disabled = true }) end lib.registerContext({ id = 'managers_menu', title = 'Verwalter verwalten', menu = 'vending_owner_menu', options = options }) lib.showContext('managers_menu') end, coords) end -- Open add manager menu function openAddManagerMenu(coords) QBCore.Functions.TriggerCallback('vending:server:getOnlinePlayers', function(players) if #players == 0 then QBCore.Functions.Notify('Keine Spieler online!', 'error') return end local options = {} for i = 1, #players do local player = players[i] table.insert(options, { title = player.name, description = 'ID: ' .. player.id, icon = 'fas fa-user', onSelect = function() TriggerServerEvent('vending:server:addManager', coords, player.id) Wait(500) openManagersMenu(coords) -- Refresh the menu end }) end lib.registerContext({ id = 'add_manager_menu', title = 'Verwalter hinzufügen', menu = 'managers_menu', options = options }) lib.showContext('add_manager_menu') end) end -- Robbery menu RegisterNetEvent('vending:client:startRobbery', function(data) local entity = data.entity local coords = GetEntityCoords(entity) lib.registerContext({ id = 'vending_robbery_confirm', title = 'Verkaufsautomat aufbrechen', options = { { title = 'Aufbrechen', description = 'Versuche den Automaten aufzubrechen', icon = 'fas fa-mask', onSelect = function() TriggerServerEvent('vending:server:startRobbery', coords) end }, { title = 'Abbrechen', description = 'Aufbruch abbrechen', icon = 'fas fa-times' } } }) lib.showContext('vending_robbery_confirm') end) -- Start robbery animation and progress RegisterNetEvent('vending:client:startRobbery', function(coords) local playerPed = PlayerPedId() local robberyTime = 10000 -- 10 seconds -- Animation RequestAnimDict('anim@heists@fleeca_bank@drilling') while not HasAnimDictLoaded('anim@heists@fleeca_bank@drilling') do Wait(100) end TaskPlayAnim(playerPed, 'anim@heists@fleeca_bank@drilling', 'drill_straight_idle', 8.0, -8.0, -1, 1, 0, false, false, false) -- Progress bar if lib.progressBar then local success = lib.progressBar({ duration = robberyTime, label = 'Automat aufbrechen...', useWhileDead = false, canCancel = true, disable = { car = true, move = true, combat = true } }) ClearPedTasks(playerPed) TriggerServerEvent('vending:server:completeRobbery', coords, success) else -- Fallback without progress bar Wait(robberyTime) ClearPedTasks(playerPed) TriggerServerEvent('vending:server:completeRobbery', coords, true) end end) -- Police alert RegisterNetEvent('vending:client:policeAlert', function(coords, streetName) local alert = { title = "Verkaufsautomat Aufbruch", coords = coords, description = "Ein Verkaufsautomat wird aufgebrochen in " .. streetName } -- Add blip local blip = AddBlipForCoord(coords.x, coords.y, coords.z) SetBlipSprite(blip, 161) SetBlipColour(blip, 1) SetBlipScale(blip, 1.0) SetBlipAsShortRange(blip, false) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Verkaufsautomat Aufbruch") EndTextCommandSetBlipName(blip) -- Remove blip after 5 minutes SetTimeout(300000, function() RemoveBlip(blip) end) QBCore.Functions.Notify('Verkaufsautomat Aufbruch gemeldet: ' .. streetName, 'error', 8000) end) -- Management menu (alternative opening method) RegisterNetEvent('vending:client:openManagement', function(machine) lib.registerContext({ id = 'vending_management', title = 'Verkaufsautomat #' .. machine.id, options = { { title = 'Inventar öffnen', description = 'Items hinzufügen oder entfernen', icon = 'fas fa-box', onSelect = function() TriggerServerEvent('vending:server:openStash', machine.coords) end }, { title = 'Einnahmen: $' .. machine.money, description = 'Geld abheben', icon = 'fas fa-money-bill', onSelect = function() openWithdrawMenu(machine.coords, machine.money) end } } }) lib.showContext('vending_management') end) -- Debug command to check props RegisterCommand('checkvendingprops', function() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local foundProps = 0 for _, propName in ipairs(Config.VendingProps) do local hash = GetHashKey(propName) local objects = GetGamePool('CObject') print("Checking for prop: " .. propName .. " (Hash: " .. hash .. ")") for _, obj in ipairs(objects) do if GetEntityModel(obj) == hash then local objCoords = GetEntityCoords(obj) local dist = #(playerCoords - objCoords) if dist < 30.0 then foundProps = foundProps + 1 print("Found " .. propName .. " at distance: " .. dist) -- Add a temporary blip local blip = AddBlipForEntity(obj) SetBlipSprite(blip, 1) SetBlipColour(blip, 2) SetBlipScale(blip, 0.8) BeginTextCommandSetBlipName("STRING") AddTextComponentString(propName) EndTextCommandSetBlipName(blip) -- Remove blip after 10 seconds SetTimeout(10000, function() RemoveBlip(blip) end) end end end end QBCore.Functions.Notify('Found ' .. foundProps .. ' vending machines nearby', 'primary') end, false) -- Debug commands RegisterCommand('vendingdebug', function() local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) QBCore.Functions.TriggerCallback('vending:server:getMachineByCoords', function(machine) if machine then print('Machine found:', json.encode(machine)) QBCore.Functions.Notify('Machine data logged to console', 'primary') else print('No machine found at current location') QBCore.Functions.Notify('No machine found here', 'error') end end, coords) end, false) -- Event to refresh machine data when a new machine is registered RegisterNetEvent('vending:client:refreshTargets', function() -- Clear cached data currentMachineData = nil end)