891 lines
		
	
	
	
		
			31 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			891 lines
		
	
	
	
		
			31 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| local QBCore = exports['qb-core']:GetCoreObject()
 | |
| local nearbyMachine = nil
 | |
| local machineData = {}
 | |
| local isInteracting = false
 | |
| 
 | |
| -- Function to draw 3D text in the world
 | |
| function DrawText3D(x, y, z, text)
 | |
|     -- Calculate distance to reduce computation when far away
 | |
|     local playerCoords = GetEntityCoords(PlayerPedId())
 | |
|     local dist = #(vector3(x, y, z) - playerCoords)
 | |
|     if dist > 5.0 then return end
 | |
|     
 | |
|     -- Set text properties
 | |
|     SetTextScale(0.35, 0.35)
 | |
|     SetTextFont(4)
 | |
|     SetTextProportional(1)
 | |
|     SetTextColour(255, 255, 255, 215)
 | |
|     SetTextOutline()
 | |
|     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
 | |
| 
 | |
| -- Get machine data with optimized callback handling
 | |
| function GetMachineData(entity)
 | |
|     local coords = GetEntityCoords(entity)
 | |
|     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|     
 | |
|     -- Return cached data if available and not expired
 | |
|     local currentTime = GetGameTimer()
 | |
|     if machineData[entityId] and (currentTime - machineData[entityId].lastCheck < 10000) then
 | |
|         return machineData[entityId]
 | |
|     end
 | |
|     
 | |
|     -- Initialize with default values
 | |
|     if not machineData[entityId] then
 | |
|         machineData[entityId] = {
 | |
|             isRegistered = false,
 | |
|             canManage = false,
 | |
|             lastCheck = currentTime,
 | |
|             checking = true
 | |
|         }
 | |
|     else
 | |
|         machineData[entityId].checking = true
 | |
|         machineData[entityId].lastCheck = currentTime
 | |
|     end
 | |
|     
 | |
|     -- Single callback to get all machine data at once (more efficient)
 | |
|     QBCore.Functions.TriggerCallback('vending:server:getMachineStatus', function(status)
 | |
|         if status then
 | |
|             machineData[entityId].isRegistered = status.exists
 | |
|             machineData[entityId].canManage = status.canManage
 | |
|         else
 | |
|             machineData[entityId].isRegistered = false
 | |
|             machineData[entityId].canManage = false
 | |
|         end
 | |
|         machineData[entityId].checking = false
 | |
|     end, coords)
 | |
|     
 | |
|     -- Short wait for callback to complete
 | |
|     local timeout = 0
 | |
|     while machineData[entityId].checking and timeout < 20 do -- Reduced timeout
 | |
|         Wait(5) -- Shorter wait
 | |
|         timeout = timeout + 1
 | |
|     end
 | |
|     
 | |
|     -- If timeout reached, set checking to false to avoid deadlock
 | |
|     if timeout >= 20 then
 | |
|         machineData[entityId].checking = false
 | |
|     end
 | |
|     
 | |
|     return machineData[entityId]
 | |
| end
 | |
| 
 | |
| -- Clear cache periodically
 | |
| CreateThread(function()
 | |
|     while true do
 | |
|         Wait(60000) -- Clear cache every minute
 | |
|         local currentTime = GetGameTimer()
 | |
|         
 | |
|         for entityId, data in pairs(machineData) do
 | |
|             if currentTime - data.lastCheck > 60000 then
 | |
|                 machineData[entityId] = nil
 | |
|             end
 | |
|         end
 | |
|     end
 | |
| 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
 | |
|                         
 | |
|                         -- Get machine data
 | |
|                         local data = GetMachineData(obj)
 | |
|                         
 | |
|                         -- Display appropriate text based on machine status
 | |
|                         local z = objCoords.z + 1.0
 | |
|                         
 | |
|                         if data.isRegistered then
 | |
|                             if data.canManage then
 | |
|                                 DrawText3D(objCoords.x, objCoords.y, z, "[E] Kaufen | [G] Verwalten")
 | |
|                                 
 | |
|                                 -- Handle key presses for management
 | |
|                                 if IsControlJustPressed(0, 38) and not isInteracting then -- E key
 | |
|                                     isInteracting = true
 | |
|                                     TriggerEvent('vending:client:openBuyMenu', {entity = obj})
 | |
|                                     Wait(500) -- Prevent multiple triggers
 | |
|                                     isInteracting = false
 | |
|                                 elseif IsControlJustPressed(0, 47) and not isInteracting 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) and not isInteracting then -- E key
 | |
|                                     isInteracting = true
 | |
|                                     TriggerEvent('vending:client:openBuyMenu', {entity = obj})
 | |
|                                     Wait(500) -- Prevent multiple triggers
 | |
|                                     isInteracting = false
 | |
|                                 elseif IsControlJustPressed(0, 47) and not isInteracting 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) and not isInteracting then -- E key
 | |
|                                 isInteracting = true
 | |
|                                 TriggerEvent('vending:client:buyMachine', {entity = obj})
 | |
|                                 Wait(500) -- Prevent multiple triggers
 | |
|                                 isInteracting = false
 | |
|                             end
 | |
|                         end
 | |
|                         break
 | |
|                     end
 | |
|                 end
 | |
|             end
 | |
|             
 | |
|             if foundMachine then break end
 | |
|         end
 | |
|         
 | |
|         Wait(wait)
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- Event to refresh machine data when a new machine is registered
 | |
| RegisterNetEvent('vending:client:refreshTargets', function()
 | |
|     -- Clear cached data
 | |
|     machineData = {}
 | |
| 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)
 | |
|                     -- Clear cache for this machine
 | |
|                     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|                     machineData[entityId] = nil
 | |
|                 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)
 | |
|     
 | |
|     -- Fast check using cached data
 | |
|     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|     if machineData[entityId] and not machineData[entityId].isRegistered then
 | |
|         QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error')
 | |
|         return
 | |
|     end
 | |
|     
 | |
|     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)
 | |
|     
 | |
|     -- Fast check using cached data
 | |
|     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|     if machineData[entityId] and not machineData[entityId].canManage then
 | |
|         QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
 | |
|         return
 | |
|     end
 | |
|     
 | |
|     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)
 | |
|         -- Clear cache for this machine
 | |
|         local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|         machineData[entityId] = nil
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Open price menu
 | |
| function openPriceMenu(coords)
 | |
|     -- Fast check using cached data
 | |
|     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|     if machineData[entityId] and not machineData[entityId].canManage then
 | |
|         QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
 | |
|         return
 | |
|     end
 | |
|     
 | |
|     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)
 | |
|     -- Fast check using cached data
 | |
|     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|     if machineData[entityId] and not machineData[entityId].canManage then
 | |
|         QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
 | |
|         return
 | |
|     end
 | |
|     
 | |
|     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)
 | |
|     -- Fast check for owner status
 | |
|     QBCore.Functions.TriggerCallback('vending:server:isOwner', function(isOwner)
 | |
|         if not isOwner then
 | |
|             QBCore.Functions.Notify('Nur der Besitzer kann Verwalter verwalten!', 'error')
 | |
|             return
 | |
|         end
 | |
|         
 | |
|         -- 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, coords)
 | |
| end
 | |
| 
 | |
| -- Open add manager menu
 | |
| function openAddManagerMenu(coords)
 | |
|     -- Fast check for owner status
 | |
|     QBCore.Functions.TriggerCallback('vending:server:isOwner', function(isOwner)
 | |
|         if not isOwner then
 | |
|             QBCore.Functions.Notify('Nur der Besitzer kann Verwalter hinzufügen!', 'error')
 | |
|             return
 | |
|         end
 | |
|         
 | |
|         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, coords)
 | |
| end
 | |
| 
 | |
| -- Robbery menu
 | |
| RegisterNetEvent('vending:client:startRobbery', function(data)
 | |
|     local entity = data.entity
 | |
|     local coords = GetEntityCoords(entity)
 | |
|     
 | |
|     -- Fast check using cached data
 | |
|     local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z)
 | |
|     if machineData[entityId] then
 | |
|         if not machineData[entityId].isRegistered then
 | |
|             QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error')
 | |
|             return
 | |
|         elseif machineData[entityId].canManage then
 | |
|             QBCore.Functions.Notify('Du kannst deinen eigenen Automaten nicht aufbrechen!', 'error')
 | |
|             return
 | |
|         end
 | |
|     end
 | |
|     
 | |
|     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 with ox_lib notification and blinking blip
 | |
| RegisterNetEvent('vending:client:policeAlert', function(alertData)
 | |
|     -- Extract data
 | |
|     local coords = alertData.coords
 | |
|     local locationName = alertData.locationName
 | |
|     
 | |
|     -- Create a blinking blip
 | |
|     local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
 | |
|     SetBlipSprite(blip, 161) -- Robbery icon
 | |
|     SetBlipColour(blip, 1) -- Red color
 | |
|     SetBlipScale(blip, 1.2)
 | |
|     SetBlipAsShortRange(blip, false)
 | |
|     
 | |
|     -- Make the blip flash
 | |
|     SetBlipFlashes(blip, true)
 | |
|     SetBlipFlashInterval(blip, 200) -- Flash interval in milliseconds
 | |
|     
 | |
|     -- Set blip name
 | |
|     BeginTextCommandSetBlipName("STRING")
 | |
|     AddTextComponentString("Verkaufsautomat Aufbruch")
 | |
|     EndTextCommandSetBlipName(blip)
 | |
|     
 | |
|     -- Create route to the robbery
 | |
|     SetBlipRoute(blip, true)
 | |
|     SetBlipRouteColour(blip, 1) -- Red route
 | |
|     
 | |
|     -- Show ox_lib notification
 | |
|     if lib and lib.notify then
 | |
|         lib.notify({
 | |
|             title = 'Verkaufsautomat Aufbruch',
 | |
|             description = 'Ein Verkaufsautomat wird aufgebrochen bei ' .. locationName,
 | |
|             type = 'error',
 | |
|             icon = 'fas fa-mask',
 | |
|             position = 'top-right',
 | |
|             duration = 8000
 | |
|         })
 | |
|     else
 | |
|         -- Fallback to QBCore notification if ox_lib is not available
 | |
|         QBCore.Functions.Notify('Verkaufsautomat Aufbruch gemeldet: ' .. locationName, 'error', 8000)
 | |
|     end
 | |
|     
 | |
|     -- Play alert sound
 | |
|     PlaySound(-1, "Lose_1st", "GTAO_FM_Events_Soundset", 0, 0, 1)
 | |
|     
 | |
|     -- Remove blip after 5 minutes
 | |
|     SetTimeout(300000, function()
 | |
|         RemoveBlip(blip)
 | |
|     end)
 | |
| end)
 | |
| 
 | |
| 
 | |
| -- Management menu (alternative opening method)
 | |
| RegisterNetEvent('vending:client:openManagement', function(machine)
 | |
|     -- Fast check for management permissions
 | |
|     QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage)
 | |
|         if not canManage then
 | |
|             QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error')
 | |
|             return
 | |
|         end
 | |
|         
 | |
|         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, machine.coords)
 | |
| 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)
 | |
| 
 | |
| -- Clear cache command for debugging
 | |
| RegisterCommand('clearvendingcache', function()
 | |
|     machineData = {}
 | |
|     QBCore.Functions.Notify('Vending machine cache cleared', 'success')
 | |
| end, false)
 | |
| 
 | |
| -- Event handler for when player loads
 | |
| RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
 | |
|     -- Clear cache when player loads
 | |
|     machineData = {}
 | |
| end)
 | |
| 
 | |
| -- Event handler for when player unloads
 | |
| RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
 | |
|     -- Clear cache when player unloads
 | |
|     machineData = {}
 | |
| end)
 | |
| 
 | |
| -- Event handler for resource start
 | |
| AddEventHandler('onResourceStart', function(resourceName)
 | |
|     if resourceName == GetCurrentResourceName() then
 | |
|         -- Clear cache when resource starts
 | |
|         machineData = {}
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- Event handler for resource stop
 | |
| AddEventHandler('onResourceStop', function(resourceName)
 | |
|     if resourceName == GetCurrentResourceName() then
 | |
|         -- Nothing to do here, but good to have for completeness
 | |
|     end
 | |
| end)
 | 
