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)
 |