local QBCore = exports['qb-core']:GetCoreObject() -- Lokale Variablen local isMenuOpen = false local currentTarget = nil local nearbyPlayers = {} -- Hilfsfunktionen local function debugPrint(message) if Config.Debug then print("^3[License-System Client] " .. message .. "^7") end end local function safeCallback(cb, ...) if cb and type(cb) == "function" then cb(...) else debugPrint("^1Callback ist keine Funktion!^7") end end local function showNotification(message, type) QBCore.Functions.Notify(message, type or 'primary') end -- Nearby Players abrufen local function getNearbyPlayers(radius) radius = radius or 5.0 local players = {} local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) for _, playerId in ipairs(GetActivePlayers()) do local targetPed = GetPlayerPed(playerId) if targetPed ~= playerPed then local targetCoords = GetEntityCoords(targetPed) local distance = #(playerCoords - targetCoords) if distance <= radius then local serverId = GetPlayerServerId(playerId) local playerName = GetPlayerName(playerId) table.insert(players, { id = serverId, name = playerName, distance = math.floor(distance * 100) / 100, ped = targetPed }) end end end -- Nach Entfernung sortieren table.sort(players, function(a, b) return a.distance < b.distance end) return players end -- Berechtigung prüfen local function hasPermission() local PlayerData = QBCore.Functions.GetPlayerData() if not PlayerData or not PlayerData.job then return false end return Config.AuthorizedJobs[PlayerData.job.name] or false end -- Lizenz anzeigen local function showLicense(licenseData) if not licenseData then showNotification(Config.Notifications.license_not_found.message, Config.Notifications.license_not_found.type) return end debugPrint("Zeige Lizenz: " .. licenseData.license.license_type) SendNUIMessage({ action = 'showLicense', data = licenseData }) SetNuiFocus(true, true) isMenuOpen = true end -- Lizenz schließen local function closeLicense() SendNUIMessage({ action = 'hideLicense' }) SetNuiFocus(false, false) isMenuOpen = false debugPrint("Lizenz geschlossen") end -- Spieler-Lizenz-Menü local function openPlayerLicenseMenu(targetId, targetName) debugPrint("Öffne Lizenz-Menü für Spieler: " .. targetName) QBCore.Functions.TriggerCallback('license-system:server:getPlayerLicenses', function(licenses) local menuOptions = {} if licenses and #licenses > 0 then for _, license in ipairs(licenses) do local licenseConfig = Config.LicenseTypes[license.license_type] or { label = license.license_type, icon = 'fas fa-id-card' } local statusIcon = license.is_active and '✅' or '❌' local statusText = license.is_active and 'Gültig' or 'Ungültig' local expireText = license.expire_date or 'Unbegrenzt' table.insert(menuOptions, { title = licenseConfig.label, description = 'Status: ' .. statusText .. ' | Gültig bis: ' .. expireText, icon = licenseConfig.icon, onSelect = function() -- Lizenz anzeigen local licenseData = { license = license, config = licenseConfig } showLicense(licenseData) end, metadata = { {label = 'Status', value = statusText}, {label = 'Ausgestellt', value = license.issue_date or 'Unbekannt'}, {label = 'Gültig bis', value = expireText}, {label = 'Aussteller', value = license.issued_by_name or 'System'} } }) end -- Aktionen hinzufügen table.insert(menuOptions, { title = '─────────────────', disabled = true }) -- Neue Lizenz ausstellen table.insert(menuOptions, { title = 'Neue Lizenz ausstellen', description = 'Eine neue Lizenz für diesen Spieler ausstellen', icon = 'fas fa-plus', onSelect = function() openIssueLicenseMenu(targetId, targetName) end }) -- Lizenz entziehen table.insert(menuOptions, { title = 'Lizenz entziehen', description = 'Eine bestehende Lizenz entziehen', icon = 'fas fa-minus', onSelect = function() openRevokeLicenseMenu(targetId, targetName, licenses) end }) else table.insert(menuOptions, { title = 'Keine Lizenzen gefunden', description = 'Dieser Spieler hat keine Lizenzen', icon = 'fas fa-exclamation-triangle', disabled = true }) table.insert(menuOptions, { title = 'Neue Lizenz ausstellen', description = 'Eine neue Lizenz für diesen Spieler ausstellen', icon = 'fas fa-plus', onSelect = function() openIssueLicenseMenu(targetId, targetName) end }) end -- Zurück-Option table.insert(menuOptions, { title = '← Zurück', icon = 'fas fa-arrow-left', onSelect = function() openLicenseMenu() end }) lib.registerContext({ id = 'player_licenses', title = 'Lizenzen: ' .. targetName, options = menuOptions }) lib.showContext('player_licenses') end, targetId) end -- Lizenz ausstellen Menü local function openIssueLicenseMenu(targetId, targetName) local menuOptions = {} for licenseType, config in pairs(Config.LicenseTypes) do table.insert(menuOptions, { title = config.label, description = config.description or 'Keine Beschreibung verfügbar', icon = config.icon, onSelect = function() if licenseType == 'drivers_license' and config.classes then openDriversLicenseClassMenu(targetId, targetName, licenseType) else confirmIssueLicense(targetId, targetName, licenseType, nil) end end, metadata = { {label = 'Preis', value = config.price .. ' $'}, {label = 'Gültigkeitsdauer', value = config.validity_days and (config.validity_days .. ' Tage') or 'Unbegrenzt'} } }) end table.insert(menuOptions, { title = '← Zurück', icon = 'fas fa-arrow-left', onSelect = function() openPlayerLicenseMenu(targetId, targetName) end }) lib.registerContext({ id = 'issue_license', title = 'Lizenz ausstellen: ' .. targetName, options = menuOptions }) lib.showContext('issue_license') end -- Führerschein-Klassen Menü local function openDriversLicenseClassMenu(targetId, targetName, licenseType) local config = Config.LicenseTypes[licenseType] local selectedClasses = {} local function updateMenu() local menuOptions = {} for _, class in ipairs(config.classes) do local isSelected = false for _, selected in ipairs(selectedClasses) do if selected == class then isSelected = true break end end local classDescriptions = { ['A'] = 'Motorräder', ['A1'] = 'Leichte Motorräder (bis 125ccm)', ['A2'] = 'Mittlere Motorräder (bis 35kW)', ['B'] = 'PKW (bis 3,5t)', ['BE'] = 'PKW mit Anhänger', ['C'] = 'LKW (über 3,5t)', ['CE'] = 'LKW mit Anhänger', ['D'] = 'Bus (über 8 Personen)', ['DE'] = 'Bus mit Anhänger' } table.insert(menuOptions, { title = 'Klasse ' .. class .. (isSelected and ' ✅' or ''), description = classDescriptions[class] or 'Keine Beschreibung', icon = isSelected and 'fas fa-check-square' or 'far fa-square', onSelect = function() if isSelected then -- Klasse entfernen for i, selected in ipairs(selectedClasses) do if selected == class then table.remove(selectedClasses, i) break end end else -- Klasse hinzufügen table.insert(selectedClasses, class) end updateMenu() end }) end table.insert(menuOptions, { title = '─────────────────', disabled = true }) table.insert(menuOptions, { title = 'Bestätigen (' .. #selectedClasses .. ' Klassen)', description = 'Führerschein mit ausgewählten Klassen ausstellen', icon = 'fas fa-check', disabled = #selectedClasses == 0, onSelect = function() confirmIssueLicense(targetId, targetName, licenseType, selectedClasses) end }) table.insert(menuOptions, { title = '← Zurück', icon = 'fas fa-arrow-left', onSelect = function() openIssueLicenseMenu(targetId, targetName) end }) lib.registerContext({ id = 'drivers_license_classes', title = 'Führerschein-Klassen: ' .. targetName, options = menuOptions }) lib.showContext('drivers_license_classes') end updateMenu() end -- Lizenz-Ausstellung bestätigen local function confirmIssueLicense(targetId, targetName, licenseType, classes) local config = Config.LicenseTypes[licenseType] local classText = classes and table.concat(classes, ', ') or 'Keine' lib.registerContext({ id = 'confirm_issue_license', title = 'Lizenz ausstellen bestätigen', options = { { title = 'Spieler: ' .. targetName, disabled = true }, { title = 'Lizenztyp: ' .. config.label, disabled = true }, { title = 'Klassen: ' .. classText, disabled = true }, { title = 'Kosten: ' .. config.price .. ' $', disabled = true }, { title = '─────────────────', disabled = true }, { title = '✅ Bestätigen', description = 'Lizenz jetzt ausstellen', icon = 'fas fa-check', onSelect = function() TriggerServerEvent('license-system:server:issueLicense', targetId, licenseType, classes) lib.hideContext() end }, { title = '❌ Abbrechen', description = 'Vorgang abbrechen', icon = 'fas fa-times', onSelect = function() openIssueLicenseMenu(targetId, targetName) end } } }) lib.showContext('confirm_issue_license') end -- Lizenz entziehen Menü local function openRevokeLicenseMenu(targetId, targetName, licenses) local menuOptions = {} for _, license in ipairs(licenses) do if license.is_active then local config = Config.LicenseTypes[license.license_type] or { label = license.license_type, icon = 'fas fa-id-card' } table.insert(menuOptions, { title = config.label, description = 'Diese Lizenz entziehen', icon = config.icon, onSelect = function() lib.registerContext({ id = 'confirm_revoke_license', title = 'Lizenz entziehen bestätigen', options = { { title = 'Spieler: ' .. targetName, disabled = true }, { title = 'Lizenztyp: ' .. config.label, disabled = true }, { title = '─────────────────', disabled = true }, { title = '✅ Bestätigen', description = 'Lizenz jetzt entziehen', icon = 'fas fa-check', onSelect = function() TriggerServerEvent('license-system:server:revokeLicense', targetId, license.license_type) lib.hideContext() end }, { title = '❌ Abbrechen', description = 'Vorgang abbrechen', icon = 'fas fa-times', onSelect = function() openRevokeLicenseMenu(targetId, targetName, licenses) end } } }) lib.showContext('confirm_revoke_license') end }) end end if #menuOptions == 0 then table.insert(menuOptions, { title = 'Keine aktiven Lizenzen', description = 'Dieser Spieler hat keine aktiven Lizenzen', icon = 'fas fa-exclamation-triangle', disabled = true }) end table.insert(menuOptions, { title = '← Zurück', icon = 'fas fa-arrow-left', onSelect = function() openPlayerLicenseMenu(targetId, targetName) end }) lib.registerContext({ id = 'revoke_license', title = 'Lizenz entziehen: ' .. targetName, options = menuOptions }) lib.showContext('revoke_license') end -- Hauptmenü für Lizenz-System local function openLicenseMenu() if not hasPermission() then showNotification(Config.Notifications.no_permission.message, Config.Notifications.no_permission.type) return end nearbyPlayers = getNearbyPlayers(5.0) if #nearbyPlayers == 0 then showNotification(Config.Notifications.no_players_nearby.message, Config.Notifications.no_players_nearby.type) return end local menuOptions = {} for _, player in ipairs(nearbyPlayers) do table.insert(menuOptions, { title = player.name, description = 'Entfernung: ' .. player.distance .. 'm', icon = 'fas fa-user', onSelect = function() openPlayerLicenseMenu(player.id, player.name) end }) end lib.registerContext({ id = 'license_nearby_players', title = 'Spieler in der Nähe (' .. #nearbyPlayers .. ')', options = menuOptions }) lib.showContext('license_nearby_players') end -- Eigene Lizenzen anzeigen local function showMyLicenses() local menuOptions = {} for licenseType, config in pairs(Config.LicenseTypes) do table.insert(menuOptions, { title = config.label, description = 'Deine ' .. config.label .. ' anzeigen', icon = config.icon, onSelect = function() QBCore.Functions.TriggerCallback('license-system:server:getMyLicense', function(licenseData) if licenseData then showLicense(licenseData) else showNotification('Du hast keine ' .. config.label .. '!', 'error') end end, licenseType) end }) end lib.registerContext({ id = 'my_licenses', title = 'Meine Lizenzen', options = menuOptions }) lib.showContext('my_licenses') end -- Events RegisterNetEvent('license-system:client:showLicense', function(targetId) QBCore.Functions.TriggerCallback('license-system:server:getLicense', function(licenseData) showLicense(licenseData) end, targetId) end) RegisterNetEvent('license-system:client:showMyLicense', function(licenseType) QBCore.Functions.TriggerCallback('license-system:server:getMyLicense', function(licenseData) showLicense(licenseData) end, licenseType) end) RegisterNetEvent('license-system:client:openCamera', function() SendNUIMessage({ action = 'openCamera' }) end) -- NUI Callbacks RegisterNUICallback('closeLicense', function(data, cb) closeLicense() safeCallback(cb, 'ok') end) RegisterNUICallback('savePhoto', function(data, cb) if data.photo and data.citizenid then TriggerServerEvent('license-system:server:savePhoto', data.citizenid, data.photo) safeCallback(cb, 'ok') else safeCallback(cb, 'error') end end) -- Commands RegisterCommand(Config.Commands.license.name, function() openLicenseMenu() end, Config.Commands.license.restricted) RegisterCommand(Config.Commands.mylicense.name, function() showMyLicenses() end, Config.Commands.mylicense.restricted) -- Keybinds if Config.Keybinds.open_license_menu then RegisterKeyMapping(Config.Commands.license.name, Config.Keybinds.open_license_menu.description, 'keyboard', Config.Keybinds.open_license_menu.key) end if Config.Keybinds.show_my_licenses then RegisterKeyMapping(Config.Commands.mylicense.name, Config.Keybinds.show_my_licenses.description, 'keyboard', Config.Keybinds.show_my_licenses.key) end -- Cleanup AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then if isMenuOpen then closeLicense() end debugPrint("License-System Client gestoppt") end end) -- Initialisierung CreateThread(function() debugPrint("License-System Client gestartet") end)