local QBCore = exports['qb-core']:GetCoreObject() -- Function to initialize targets function InitializeTargets() -- Remove existing targets first to avoid duplicates exports['qb-target']:RemoveTargetModel(Config.VendingProps) Wait(100) -- Add targets exports['qb-target']:AddTargetModel(Config.VendingProps, { options = { { type = "client", event = "vending:client:buyMachine", icon = "fas fa-dollar-sign", label = "Automaten kaufen ($" .. Config.VendingMachinePrice .. ")", canInteract = function(entity) return not isRegisteredMachine(entity) end }, { type = "client", event = "vending:client:openBuyMenu", icon = "fas fa-shopping-cart", label = "Kaufen", canInteract = function(entity) return isRegisteredMachine(entity) end }, { type = "client", event = "vending:client:openOwnerMenu", icon = "fas fa-cog", label = "Verwalten", canInteract = function(entity) return canManageMachine(entity) end }, { type = "client", event = "vending:client:startRobbery", icon = "fas fa-mask", label = "Aufbrechen", canInteract = function(entity) return isRegisteredMachine(entity) and not canManageMachine(entity) end } }, distance = 2.0 }) print("^2[VENDING]^7 Added targets to " .. #Config.VendingProps .. " vending machine props") end -- Add targets to all vending machine props with multiple attempts (Option 1) CreateThread(function() -- First attempt Wait(2000) InitializeTargets() -- Second attempt after a delay Wait(5000) InitializeTargets() -- Third attempt after server is fully loaded Wait(10000) InitializeTargets() end) -- Event-based initialization (Option 2) RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() Wait(1000) InitializeTargets() end) RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() -- Nothing to do here, but good to have for completeness end) -- Listen for resource start/stop events AddEventHandler('onResourceStart', function(resourceName) if resourceName == 'qb-target' or resourceName == GetCurrentResourceName() then Wait(1000) InitializeTargets() end end) -- Command to manually refresh targets RegisterCommand('refreshvendingtargets', function() InitializeTargets() QBCore.Functions.Notify('Vending machine targets refreshed', 'success') end, false) -- 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 (not ideal but works for canInteract) 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 -- 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) -- Funktion zum Verkaufen des Automaten 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) -- Refresh targets (called when new machine is registered) RegisterNetEvent('vending:client:refreshTargets', function() InitializeTargets() 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)