From cee2e0b71072f491042ad84c013b161afdbad8c6 Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Tue, 29 Jul 2025 22:47:43 +0200 Subject: [PATCH] Update client.lua --- .../[inventory]/nordi_vending/client.lua | 1156 +++++++++++------ 1 file changed, 779 insertions(+), 377 deletions(-) diff --git a/resources/[inventory]/nordi_vending/client.lua b/resources/[inventory]/nordi_vending/client.lua index 50dde2966..aecc4eea5 100644 --- a/resources/[inventory]/nordi_vending/client.lua +++ b/resources/[inventory]/nordi_vending/client.lua @@ -1,29 +1,30 @@ local QBCore = exports['qb-core']:GetCoreObject() local nearbyMachine = nil -local displayingText = false +local currentMachineData = nil +local isInteracting = false -- Function to draw 3D text in the world function DrawText3D(x, y, z, text) - local onScreen, _x, _y = World3dToScreen2d(x, y, z) - local px, py, pz = table.unpack(GetGameplayCamCoords()) - local scale = 0.35 - local font = 4 - - if onScreen then - SetTextScale(scale, scale) - SetTextFont(font) - SetTextProportional(1) - SetTextColour(255, 255, 255, 215) - SetTextOutline() - SetTextEntry("STRING") - SetTextCentre(1) - AddTextComponentString(text) - DrawText(_x, _y) - end + SetTextScale(0.35, 0.35) + SetTextFont(4) + SetTextProportional(1) + SetTextColour(255, 255, 255, 215) + SetTextEntry("STRING") + SetTextCentre(1) + AddTextComponentString(text) + SetDrawOrigin(x, y, z, 0) + DrawText(0.0, 0.0) + local factor = (string.len(text)) / 370 + DrawRect(0.0, 0.0+0.0125, 0.017+ factor, 0.03, 0, 0, 0, 75) + ClearDrawOrigin() end --- Function to check if machine is registered +-- Function to check if machine is registered (with caching) function isRegisteredMachine(entity) + if currentMachineData and currentMachineData.entity == entity then + return currentMachineData.isRegistered + end + local coords = GetEntityCoords(entity) local isRegistered = false @@ -31,18 +32,26 @@ function isRegisteredMachine(entity) isRegistered = exists end, coords) - -- Wait for callback (not ideal but works for canInteract) + -- Wait for callback local timeout = 0 while isRegistered == false and timeout < 100 do Wait(10) timeout = timeout + 1 end + if not currentMachineData then currentMachineData = {} end + currentMachineData.entity = entity + currentMachineData.isRegistered = isRegistered + return isRegistered end --- Check if player can manage machine +-- Check if player can manage machine (with caching) function canManageMachine(entity) + if currentMachineData and currentMachineData.entity == entity and currentMachineData.canManage ~= nil then + return currentMachineData.canManage + end + local coords = GetEntityCoords(entity) local canManage = false @@ -57,6 +66,10 @@ function canManageMachine(entity) timeout = timeout + 1 end + if not currentMachineData then currentMachineData = {} end + currentMachineData.entity = entity + currentMachineData.canManage = canManage + return canManage end @@ -66,7 +79,7 @@ CreateThread(function() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local wait = 1000 - nearbyMachine = nil + local foundMachine = false -- Check for nearby vending machines for _, propName in ipairs(Config.VendingProps) do @@ -80,61 +93,87 @@ CreateThread(function() if dist < 2.0 then wait = 0 + foundMachine = true nearbyMachine = obj - -- Display interaction options based on conditions - if not displayingText then - displayingText = true - CreateThread(function() - while nearbyMachine == obj and #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(obj)) < 2.0 do - local z = objCoords.z + 1.0 + -- Only check status if not already interacting + if not isInteracting then + local z = objCoords.z + 1.0 + local registered = isRegisteredMachine(obj) + + if registered then + local canManage = canManageMachine(obj) + + if canManage then + DrawText3D(objCoords.x, objCoords.y, z, "[E] Kaufen | [G] Verwalten") - -- Check conditions and display appropriate text - if isRegisteredMachine(obj) then - if canManageMachine(obj) then - DrawText3D(objCoords.x, objCoords.y, z, "~g~E~w~ - Kaufen | ~y~G~w~ - Verwalten") - - -- Handle key presses for management - if IsControlJustReleased(0, 38) then -- E key - TriggerEvent('vending:client:openBuyMenu', {entity = obj}) - elseif IsControlJustReleased(0, 47) then -- G key - TriggerEvent('vending:client:openOwnerMenu', {entity = obj}) - end - else - DrawText3D(objCoords.x, objCoords.y, z, "~g~E~w~ - Kaufen | ~r~G~w~ - Aufbrechen") - - -- Handle key presses for buying/robbery - if IsControlJustReleased(0, 38) then -- E key - TriggerEvent('vending:client:openBuyMenu', {entity = obj}) - elseif IsControlJustReleased(0, 47) then -- G key - TriggerEvent('vending:client:startRobbery', {entity = obj}) - end - end - else - DrawText3D(objCoords.x, objCoords.y, z, "~g~E~w~ - Automaten kaufen ($" .. Config.VendingMachinePrice .. ")") - - -- Handle key press for buying machine - if IsControlJustReleased(0, 38) then -- E key - TriggerEvent('vending:client:buyMachine', {entity = obj}) - end + -- Handle key presses for management + if IsControlJustPressed(0, 38) then -- E key + isInteracting = true + TriggerEvent('vending:client:openBuyMenu', {entity = obj}) + Wait(500) -- Prevent multiple triggers + isInteracting = false + elseif IsControlJustPressed(0, 47) then -- G key + isInteracting = true + TriggerEvent('vending:client:openOwnerMenu', {entity = obj}) + Wait(500) -- Prevent multiple triggers + isInteracting = false + end + else + DrawText3D(objCoords.x, objCoords.y, z, "[E] Kaufen | [G] Aufbrechen") + + -- Handle key presses for buying/robbery + if IsControlJustPressed(0, 38) then -- E key + isInteracting = true + TriggerEvent('vending:client:openBuyMenu', {entity = obj}) + Wait(500) -- Prevent multiple triggers + isInteracting = false + elseif IsControlJustPressed(0, 47) then -- G key + isInteracting = true + TriggerEvent('vending:client:startRobbery', {entity = obj}) + Wait(500) -- Prevent multiple triggers + isInteracting = false end - Wait(0) end - displayingText = false - end) + else + DrawText3D(objCoords.x, objCoords.y, z, "[E] Automaten kaufen ($" .. Config.VendingMachinePrice .. ")") + + -- Handle key press for buying machine + if IsControlJustPressed(0, 38) then -- E key + isInteracting = true + TriggerEvent('vending:client:buyMachine', {entity = obj}) + Wait(500) -- Prevent multiple triggers + isInteracting = false + end + end end break end end end - if nearbyMachine then break end + if foundMachine then break end + end + + if not foundMachine then + nearbyMachine = nil + currentMachineData = nil end Wait(wait) end end) +-- Reset machine data when moving away +CreateThread(function() + while true do + Wait(5000) + if not nearbyMachine then + currentMachineData = nil + end + end +end) + -- Buy vending machine RegisterNetEvent('vending:client:buyMachine', function(data) local entity = data.entity @@ -152,27 +191,32 @@ RegisterNetEvent('vending:client:buyMachine', function(data) 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' - } - } - }) + -- Display confirmation text + local startTime = GetGameTimer() + local textShown = true - lib.showContext('vending_buy_confirm') + CreateThread(function() + while textShown do + local objCoords = GetEntityCoords(entity) + DrawText3D(objCoords.x, objCoords.y, objCoords.z + 1.0, "Automaten für $" .. Config.VendingMachinePrice .. " kaufen?\n[Y] Ja | [N] Nein") + Wait(0) + end + end) + + -- Wait for key press + while GetGameTimer() - startTime < 10000 and textShown do + if IsControlJustPressed(0, 246) then -- Y key + textShown = false + TriggerServerEvent('vending:server:registerMachine', coords, prop) + break + elseif IsControlJustPressed(0, 249) then -- N key + textShown = false + break + end + Wait(0) + end + + textShown = false end) -- Open buy menu with quantity selection @@ -186,19 +230,19 @@ RegisterNetEvent('vending:client:openBuyMenu', function(data) return end + -- Simple item selection menu local options = {} + local selectedItem = nil 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 + name = item.name, + label = itemLabel, + price = item.price, + amount = item.amount }) end end @@ -208,40 +252,84 @@ RegisterNetEvent('vending:client:openBuyMenu', function(data) return end - lib.registerContext({ - id = 'vending_buy_menu', - title = 'Verkaufsautomat', - options = options - }) + -- Display item selection + local currentIndex = 1 + local menuActive = true - lib.showContext('vending_buy_menu') + CreateThread(function() + while menuActive do + local objCoords = GetEntityCoords(entity) + local item = options[currentIndex] + + DrawText3D(objCoords.x, objCoords.y, objCoords.z + 1.0, + "Item: " .. item.label .. "\nPreis: $" .. item.price .. " | Verfügbar: " .. item.amount .. + "\n[←][→] Navigieren | [ENTER] Auswählen | [ESC] Abbrechen") + + Wait(0) + end + end) + + -- Handle navigation + while menuActive do + if IsControlJustPressed(0, 174) then -- Left arrow + currentIndex = currentIndex - 1 + if currentIndex < 1 then currentIndex = #options end + Wait(200) + elseif IsControlJustPressed(0, 175) then -- Right arrow + currentIndex = currentIndex + 1 + if currentIndex > #options then currentIndex = 1 end + Wait(200) + elseif IsControlJustPressed(0, 18) then -- Enter key + selectedItem = options[currentIndex] + menuActive = false + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + return + end + Wait(0) + end + + -- If item selected, ask for quantity + if selectedItem then + Wait(100) -- Small delay + + -- Display quantity selection + local quantity = 1 + local quantityMenu = true + + CreateThread(function() + while quantityMenu do + local objCoords = GetEntityCoords(entity) + DrawText3D(objCoords.x, objCoords.y, objCoords.z + 1.0, + "Item: " .. selectedItem.label .. " | Preis: $" .. (selectedItem.price * quantity) .. + "\nMenge: " .. quantity .. "/" .. selectedItem.amount .. + "\n[←][→] Ändern | [ENTER] Kaufen | [ESC] Abbrechen") + Wait(0) + end + end) + + -- Handle quantity selection + while quantityMenu do + if IsControlJustPressed(0, 174) then -- Left arrow + quantity = quantity - 1 + if quantity < 1 then quantity = 1 end + Wait(100) + elseif IsControlJustPressed(0, 175) then -- Right arrow + quantity = quantity + 1 + if quantity > selectedItem.amount then quantity = selectedItem.amount end + Wait(100) + elseif IsControlJustPressed(0, 18) then -- Enter key + quantityMenu = false + TriggerServerEvent('vending:server:buyItem', coords, selectedItem.name, quantity) + elseif IsControlJustPressed(0, 177) then -- Escape key + quantityMenu = false + end + Wait(0) + end + end 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 @@ -253,86 +341,104 @@ RegisterNetEvent('vending:client:openOwnerMenu', function(data) return end + -- Display owner menu options 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 - } + {label = "Inventar verwalten", action = "inventory"}, + {label = "Preise festlegen", action = "prices"}, + {label = "Geld abheben ($" .. machine.money .. ")", action = "withdraw"}, + {label = "Statistiken", action = "stats"} } -- 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 - }) + table.insert(options, {label = "Verwalter", action = "managers"}) + table.insert(options, {label = "Automaten verkaufen", action = "sell"}) end - lib.registerContext({ - id = 'vending_owner_menu', - title = 'Verkaufsautomat Verwaltung', - options = options - }) + -- Display menu + local currentIndex = 1 + local menuActive = true - lib.showContext('vending_owner_menu') + CreateThread(function() + while menuActive do + local objCoords = GetEntityCoords(entity) + local menuText = "Verkaufsautomat Verwaltung\n" + + for i, option in ipairs(options) do + if i == currentIndex then + menuText = menuText .. "→ " .. option.label .. "\n" + else + menuText = menuText .. option.label .. "\n" + end + end + + menuText = menuText .. "\n[↑][↓] Navigieren | [ENTER] Auswählen | [ESC] Abbrechen" + + DrawText3D(objCoords.x, objCoords.y, objCoords.z + 1.0, menuText) + Wait(0) + end + end) + + -- Handle navigation + while menuActive do + if IsControlJustPressed(0, 172) then -- Up arrow + currentIndex = currentIndex - 1 + if currentIndex < 1 then currentIndex = #options end + Wait(200) + elseif IsControlJustPressed(0, 173) then -- Down arrow + currentIndex = currentIndex + 1 + if currentIndex > #options then currentIndex = 1 end + Wait(200) + elseif IsControlJustPressed(0, 18) then -- Enter key + local selectedOption = options[currentIndex].action + menuActive = false + + -- Handle selected option + if selectedOption == "inventory" then + TriggerServerEvent('vending:server:openStash', coords) + elseif selectedOption == "prices" then + openPriceMenu(coords) + elseif selectedOption == "withdraw" then + openWithdrawMenu(coords, machine.money) + elseif selectedOption == "stats" then + openStatsMenu(machine) + elseif selectedOption == "managers" then + openManagersMenu(coords) + elseif selectedOption == "sell" then + sellVendingMachine(coords, machine.id) + end + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + end + Wait(0) + end 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 - } - }) + -- Display confirmation text + local confirmActive = true - if input and input[1] then - TriggerServerEvent('vending:server:sellMachine', coords, machineId) + CreateThread(function() + while confirmActive do + DrawText3D(coords.x, coords.y, coords.z + 1.0, + "Automaten verkaufen für $" .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. "?\n" .. + "Diese Aktion kann nicht rückgängig gemacht werden!\n" .. + "[Y] Bestätigen | [N] Abbrechen") + Wait(0) + end + end) + + -- Wait for confirmation + while confirmActive do + if IsControlJustPressed(0, 246) then -- Y key + confirmActive = false + TriggerServerEvent('vending:server:sellMachine', coords, machineId) + elseif IsControlJustPressed(0, 249) then -- N key + confirmActive = false + end + Wait(0) end end @@ -344,47 +450,122 @@ function openPriceMenu(coords) return end + -- Create options list 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 + name = item.name, + label = itemLabel, + price = item.price }) end - lib.registerContext({ - id = 'vending_price_menu', - title = 'Preise festlegen', - menu = 'vending_owner_menu', - options = options - }) + -- Display menu + local currentIndex = 1 + local menuActive = true - lib.showContext('vending_price_menu') + CreateThread(function() + while menuActive do + local menuText = "Preise festlegen\n" + + for i, option in ipairs(options) do + if i == currentIndex then + menuText = menuText .. "→ " .. option.label .. " - $" .. option.price .. "\n" + else + menuText = menuText .. option.label .. " - $" .. option.price .. "\n" + end + end + + menuText = menuText .. "\n[↑][↓] Navigieren | [ENTER] Preis ändern | [ESC] Zurück" + + DrawText3D(coords.x, coords.y, coords.z + 1.0, menuText) + Wait(0) + end + end) + + -- Handle navigation + while menuActive do + if IsControlJustPressed(0, 172) then -- Up arrow + currentIndex = currentIndex - 1 + if currentIndex < 1 then currentIndex = #options end + Wait(200) + elseif IsControlJustPressed(0, 173) then -- Down arrow + currentIndex = currentIndex + 1 + if currentIndex > #options then currentIndex = 1 end + Wait(200) + elseif IsControlJustPressed(0, 18) then -- Enter key + local selectedItem = options[currentIndex] + menuActive = false + setPriceForItem(coords, selectedItem.name, selectedItem.label) + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + end + Wait(0) + end 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 - } - }) + -- Display price input + local price = 0 + local inputActive = true + local inputText = "" - if input and input[1] then - TriggerServerEvent('vending:server:setItemPrice', coords, itemName, tonumber(input[1])) + CreateThread(function() + while inputActive do + DrawText3D(coords.x, coords.y, coords.z + 1.0, + "Preis für " .. itemLabel .. " festlegen\n" .. + "Aktueller Wert: $" .. inputText .. "_\n" .. + "Verwende Nummerntasten | [ENTER] Bestätigen | [ESC] Abbrechen") + Wait(0) + end + end) + + -- Handle input + while inputActive do + -- Number keys (0-9) + for i = 48, 57 do + if IsControlJustPressed(0, i) then + inputText = inputText .. (i - 48) + Wait(200) + end + end + + -- Numpad keys (0-9) + for i = 96, 105 do + if IsControlJustPressed(0, i) then + inputText = inputText .. (i - 96) + Wait(200) + end + end + + -- Backspace + if IsControlJustPressed(0, 194) and string.len(inputText) > 0 then + inputText = string.sub(inputText, 1, string.len(inputText) - 1) + Wait(200) + end + + -- Enter + if IsControlJustPressed(0, 18) then + inputActive = false + price = tonumber(inputText) or 0 + if price > 0 then + TriggerServerEvent('vending:server:setItemPrice', coords, itemName, price) + else + QBCore.Functions.Notify('Ungültiger Preis!', 'error') + end + end + + -- Escape + if IsControlJustPressed(0, 177) then + inputActive = false + end + + Wait(0) end end @@ -395,147 +576,248 @@ function openWithdrawMenu(coords, availableMoney) 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 - } - }) + -- Display withdraw input + local inputActive = true + local inputText = "" - if input and input[1] then - TriggerServerEvent('vending:server:withdrawMoney', coords, tonumber(input[1])) + CreateThread(function() + while inputActive do + DrawText3D(coords.x, coords.y, coords.z + 1.0, + "Geld abheben (Verfügbar: $" .. availableMoney .. ")\n" .. + "Betrag: $" .. inputText .. "_\n" .. + "Verwende Nummerntasten | [ENTER] Bestätigen | [ESC] Abbrechen") + Wait(0) + end + end) + + -- Handle input + while inputActive do + -- Number keys (0-9) + for i = 48, 57 do + if IsControlJustPressed(0, i) then + inputText = inputText .. (i - 48) + Wait(200) + end + end + + -- Numpad keys (0-9) + for i = 96, 105 do + if IsControlJustPressed(0, i) then + inputText = inputText .. (i - 96) + Wait(200) + end + end + + -- Backspace + if IsControlJustPressed(0, 194) and string.len(inputText) > 0 then + inputText = string.sub(inputText, 1, string.len(inputText) - 1) + Wait(200) + end + + -- Enter + if IsControlJustPressed(0, 18) then + inputActive = false + local amount = tonumber(inputText) or 0 + if amount > 0 and amount <= availableMoney then + TriggerServerEvent('vending:server:withdrawMoney', coords, amount) + else + QBCore.Functions.Notify('Ungültiger Betrag!', 'error') + end + end + + -- Escape + if IsControlJustPressed(0, 177) then + inputActive = false + end + + Wait(0) 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' - } - } - }) + -- Display stats + local menuActive = true - lib.showContext('vending_stats_menu') + CreateThread(function() + while menuActive do + DrawText3D(machine.coords.x, machine.coords.y, machine.coords.z + 1.0, + "Verkaufsstatistiken\n" .. + "Gesamteinnahmen: $" .. machine.money .. "\n" .. + "Automat ID: #" .. machine.id .. "\n" .. + "Standort: X:" .. math.floor(machine.coords.x) .. " Y:" .. math.floor(machine.coords.y) .. "\n" .. + "[ESC] Zurück") + Wait(0) + end + end) + + -- Wait for escape key + while menuActive do + if IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + end + Wait(0) + end end -- Open managers menu function openManagersMenu(coords) -- Get current managers QBCore.Functions.TriggerCallback('vending:server:getManagers', function(managers) + -- Create options list local options = { - { - title = 'Verwalter hinzufügen', - description = 'Neuen Verwalter hinzufügen', - icon = 'fas fa-user-plus', - onSelect = function() - openAddManagerMenu(coords) - end - } + {label = "Verwalter hinzufügen", action = "add"} } - -- Add existing managers with remove option + -- Add existing managers 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 + label = manager.name .. (manager.online and " (Online)" or " (Offline)"), + citizenid = manager.citizenid, + action = "manage" }) end else - table.insert(options, { - title = 'Keine Verwalter', - description = 'Es sind keine Verwalter vorhanden', - icon = 'fas fa-info-circle', - disabled = true - }) + table.insert(options, {label = "Keine Verwalter", action = "none"}) end - lib.registerContext({ - id = 'managers_menu', - title = 'Verwalter verwalten', - menu = 'vending_owner_menu', - options = options - }) + -- Display menu + local currentIndex = 1 + local menuActive = true - lib.showContext('managers_menu') + CreateThread(function() + while menuActive do + local menuText = "Verwalter verwalten\n" + + for i, option in ipairs(options) do + if i == currentIndex then + menuText = menuText .. "→ " .. option.label .. "\n" + else + menuText = menuText .. option.label .. "\n" + end + end + + menuText = menuText .. "\n[↑][↓] Navigieren | [ENTER] Auswählen | [ESC] Zurück" + + DrawText3D(coords.x, coords.y, coords.z + 1.0, menuText) + Wait(0) + end + end) + + -- Handle navigation + while menuActive do + if IsControlJustPressed(0, 172) then -- Up arrow + currentIndex = currentIndex - 1 + if currentIndex < 1 then currentIndex = #options end + Wait(200) + elseif IsControlJustPressed(0, 173) then -- Down arrow + currentIndex = currentIndex + 1 + if currentIndex > #options then currentIndex = 1 end + Wait(200) + elseif IsControlJustPressed(0, 18) then -- Enter key + local selectedOption = options[currentIndex] + + if selectedOption.action == "add" then + menuActive = false + openAddManagerMenu(coords) + elseif selectedOption.action == "manage" then + menuActive = false + openManagerOptionsMenu(coords, selectedOption.citizenid) + end + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + end + Wait(0) + end end, coords) end +-- Open manager options menu +function openManagerOptionsMenu(coords, citizenid) + -- Display options + local menuActive = true + + CreateThread(function() + while menuActive do + DrawText3D(coords.x, coords.y, coords.z + 1.0, + "Verwalter Optionen\n" .. + "[E] Entfernen\n" .. + "[ESC] Zurück") + Wait(0) + end + end) + + -- Handle input + while menuActive do + if IsControlJustPressed(0, 38) then -- E key + menuActive = false + TriggerServerEvent('vending:server:removeManager', coords, citizenid) + Wait(500) + openManagersMenu(coords) -- Refresh the menu + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + openManagersMenu(coords) -- Go back to managers menu + end + Wait(0) + end +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') + openManagersMenu(coords) -- Go back to managers menu return end - local options = {} + -- Display player selection + local currentIndex = 1 + local menuActive = true - 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 + CreateThread(function() + while menuActive do + local menuText = "Verwalter hinzufügen\n" + + for i, player in ipairs(players) do + if i == currentIndex then + menuText = menuText .. "→ " .. player.name .. " (ID: " .. player.id .. ")\n" + else + menuText = menuText .. player.name .. " (ID: " .. player.id .. ")\n" + end end - }) + + menuText = menuText .. "\n[↑][↓] Navigieren | [ENTER] Auswählen | [ESC] Zurück" + + DrawText3D(coords.x, coords.y, coords.z + 1.0, menuText) + Wait(0) + end + end) + + -- Handle navigation + while menuActive do + if IsControlJustPressed(0, 172) then -- Up arrow + currentIndex = currentIndex - 1 + if currentIndex < 1 then currentIndex = #players end + Wait(200) + elseif IsControlJustPressed(0, 173) then -- Down arrow + currentIndex = currentIndex + 1 + if currentIndex > #players then currentIndex = 1 end + Wait(200) + elseif IsControlJustPressed(0, 18) then -- Enter key + local selectedPlayer = players[currentIndex] + menuActive = false + TriggerServerEvent('vending:server:addManager', coords, selectedPlayer.id) + Wait(500) + openManagersMenu(coords) -- Refresh the menu + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + openManagersMenu(coords) -- Go back to managers menu + end + Wait(0) end - - lib.registerContext({ - id = 'add_manager_menu', - title = 'Verwalter hinzufügen', - menu = 'managers_menu', - options = options - }) - - lib.showContext('add_manager_menu') end) end @@ -544,27 +826,31 @@ 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' - } - } - }) + -- Display robbery confirmation + local menuActive = true - lib.showContext('vending_robbery_confirm') + CreateThread(function() + while menuActive do + DrawText3D(coords.x, coords.y, coords.z + 1.0, + "Verkaufsautomat aufbrechen\n" .. + "[E] Aufbrechen\n" .. + "[ESC] Abbrechen") + Wait(0) + end + end) + + -- Handle input + while menuActive do + if IsControlJustPressed(0, 38) then -- E key + menuActive = false + TriggerServerEvent('vending:server:startRobbery', coords) + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + end + Wait(0) + end end) -- Start robbery animation and progress @@ -580,38 +866,49 @@ RegisterNetEvent('vending:client:startRobbery', function(coords) 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) + -- Progress bar (native implementation) + local startTime = GetGameTimer() + local endTime = startTime + robberyTime + local cancelled = false + + -- Display progress bar + CreateThread(function() + while GetGameTimer() < endTime and not cancelled do + local timeLeft = endTime - GetGameTimer() + local progress = 1.0 - (timeLeft / robberyTime) + + DrawRect(0.5, 0.95, 0.2, 0.03, 0, 0, 0, 180) + DrawRect(0.5 - ((1.0 - progress) * 0.1), 0.95, 0.2 * progress, 0.03, 255, 0, 0, 180) + + SetTextScale(0.35, 0.35) + SetTextFont(4) + SetTextProportional(1) + SetTextColour(255, 255, 255, 215) + SetTextEntry("STRING") + SetTextCentre(1) + AddTextComponentString("Automat aufbrechen... " .. math.floor(progress * 100) .. "%") + DrawText(0.5, 0.94) + + -- Check for cancel + if IsControlJustPressed(0, 177) then -- Escape key + cancelled = true + end + + Wait(0) + end + end) + + -- Wait for completion + while GetGameTimer() < endTime and not cancelled do + Wait(100) end + + ClearPedTasks(playerPed) + TriggerServerEvent('vending:server:completeRobbery', coords, not cancelled) 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) @@ -632,30 +929,58 @@ 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 - } - } - }) + -- Display management menu + local options = { + {label = "Inventar öffnen", action = "inventory"}, + {label = "Geld abheben ($" .. machine.money .. ")", action = "withdraw"} + } - lib.showContext('vending_management') + local currentIndex = 1 + local menuActive = true + + CreateThread(function() + while menuActive do + local menuText = "Verkaufsautomat #" .. machine.id .. "\n" + + for i, option in ipairs(options) do + if i == currentIndex then + menuText = menuText .. "→ " .. option.label .. "\n" + else + menuText = menuText .. option.label .. "\n" + end + end + + menuText = menuText .. "\n[↑][↓] Navigieren | [ENTER] Auswählen | [ESC] Abbrechen" + + DrawText3D(machine.coords.x, machine.coords.y, machine.coords.z + 1.0, menuText) + Wait(0) + end + end) + + -- Handle navigation + while menuActive do + if IsControlJustPressed(0, 172) then -- Up arrow + currentIndex = currentIndex - 1 + if currentIndex < 1 then currentIndex = #options end + Wait(200) + elseif IsControlJustPressed(0, 173) then -- Down arrow + currentIndex = currentIndex + 1 + if currentIndex > #options then currentIndex = 1 end + Wait(200) + elseif IsControlJustPressed(0, 18) then -- Enter key + local selectedOption = options[currentIndex].action + menuActive = false + + if selectedOption == "inventory" then + TriggerServerEvent('vending:server:openStash', machine.coords) + elseif selectedOption == "withdraw" then + openWithdrawMenu(machine.coords, machine.money) + end + elseif IsControlJustPressed(0, 177) then -- Escape key + menuActive = false + end + Wait(0) + end end) -- Debug command to check props @@ -715,3 +1040,80 @@ RegisterCommand('vendingdebug', function() end end, coords) end, false) + +-- Helper function to display notifications +function ShowHelpNotification(text) + BeginTextCommandDisplayHelp("STRING") + AddTextComponentSubstringPlayerName(text) + EndTextCommandDisplayHelp(0, 0, 1, -1) +end + +-- Helper function to get key name +function GetKeyName(key) + local keyNames = { + [38] = "E", + [47] = "G", + [172] = "↑", + [173] = "↓", + [174] = "←", + [175] = "→", + [18] = "ENTER", + [177] = "ESC", + [246] = "Y", + [249] = "N" + } + return keyNames[key] or "KEY " .. key +end + +-- Helper function to handle text input +function HandleTextInput(maxLength) + local input = "" + local inputActive = true + + while inputActive do + -- Number keys (0-9) + for i = 48, 57 do + if IsControlJustPressed(0, i) and string.len(input) < maxLength then + input = input .. (i - 48) + Wait(200) + end + end + + -- Numpad keys (0-9) + for i = 96, 105 do + if IsControlJustPressed(0, i) and string.len(input) < maxLength then + input = input .. (i - 96) + Wait(200) + end + end + + -- Backspace + if IsControlJustPressed(0, 194) and string.len(input) > 0 then + input = string.sub(input, 1, string.len(input) - 1) + Wait(200) + end + + -- Enter + if IsControlJustPressed(0, 18) then + inputActive = false + return input + end + + -- Escape + if IsControlJustPressed(0, 177) then + inputActive = false + return nil + end + + Wait(0) + end + + return nil +end + +-- Event to refresh machine data when a new machine is registered +RegisterNetEvent('vending:client:refreshTargets', function() + -- Clear cached data + currentMachineData = nil +end) +