diff --git a/resources/[carscripts]/nordi_car_admin/client.lua b/resources/[carscripts]/nordi_car_admin/client.lua index 068c016c0..366c2b2ac 100644 --- a/resources/[carscripts]/nordi_car_admin/client.lua +++ b/resources/[carscripts]/nordi_car_admin/client.lua @@ -61,8 +61,6 @@ local function ShowVehicleActions(vehicle, playerJob) actionOptions = { {value = 'move', label = '🏠 In Garage stellen'}, {value = 'delete', label = '🗑️ Von Map löschen'}, - {value = 'impound', label = '🚫 Beschlagnahmen'}, - {value = 'release', label = '✅ Aus Beschlagnahme freigeben'}, {value = 'repair', label = '🔧 Reparieren'} } elseif playerJob.jobName == 'mechanic' then @@ -87,15 +85,13 @@ local function ShowVehicleActions(vehicle, playerJob) actionOptions = { {value = 'move', label = '🏠 In Garage stellen'}, {value = 'delete', label = '🗑️ Von Map löschen'}, - {value = 'repair', label = '🔧 Reparieren'}, - {value = 'impound', label = '🚫 Beschlagnahmen'}, - {value = 'release', label = '✅ Aus Beschlagnahme freigeben'} + {value = 'repair', label = '🔧 Reparieren'} } - end + } local vehicleLabel = QBCore.Shared.Vehicles[vehicle.vehicle]?.name or vehicle.vehicle - local input = lib.inputDialog('Fahrzeug Aktionen - ' .. vehicleLabel .. ' (' .. vehicle.plate .. ')', { + local input = lib.inputDialog('Fahrzeug Aktionen - ' .. (vehicle.name or vehicleLabel) .. ' (' .. vehicle.plate .. ')', { { type = 'select', label = 'Aktion wählen (' .. playerJob.jobLabel .. ')', @@ -106,7 +102,7 @@ local function ShowVehicleActions(vehicle, playerJob) { type = 'select', label = 'Garage auswählen', - description = 'Nur für "In Garage stellen" und "Freigeben" benötigt', + description = 'Nur für "In Garage stellen" benötigt', options = garageOptions, required = false } @@ -129,7 +125,7 @@ local function ShowVehicleActions(vehicle, playerJob) elseif action == 'delete' then local alert = lib.alertDialog({ header = 'Fahrzeug löschen', - content = 'Bist du sicher, dass du das Fahrzeug ' .. vehicleLabel .. ' (' .. vehicle.plate .. ') von der Map löschen möchtest?', + content = 'Bist du sicher, dass du das Fahrzeug ' .. (vehicle.name or vehicleLabel) .. ' (' .. vehicle.plate .. ') von der Map löschen möchtest?', centered = true, cancel = true }) @@ -139,41 +135,13 @@ local function ShowVehicleActions(vehicle, playerJob) elseif action == 'repair' then local alert = lib.alertDialog({ header = 'Fahrzeug reparieren', - content = 'Möchtest du das Fahrzeug ' .. vehicleLabel .. ' (' .. vehicle.plate .. ') vollständig reparieren?', + content = 'Möchtest du das Fahrzeug ' .. (vehicle.name or vehicleLabel) .. ' (' .. vehicle.plate .. ') vollständig reparieren?', centered = true, cancel = true }) if alert == 'confirm' then TriggerServerEvent('vehicleadmin:repairVehicle', vehicle.plate) end - elseif action == 'impound' then - local alert = lib.alertDialog({ - header = 'Fahrzeug beschlagnahmen', - content = 'Möchtest du das Fahrzeug ' .. vehicleLabel .. ' (' .. vehicle.plate .. ') beschlagnahmen? (500€ Gebühr)', - centered = true, - cancel = true - }) - if alert == 'confirm' then - TriggerServerEvent('vehicleadmin:impoundVehicle', vehicle.plate) - end - elseif action == 'release' then - if not garage then - lib.notify({ - title = 'Fehler', - description = 'Bitte wähle eine Garage für die Freigabe aus', - type = 'error' - }) - return - end - local alert = lib.alertDialog({ - header = 'Fahrzeug freigeben', - content = 'Möchtest du das Fahrzeug ' .. vehicleLabel .. ' (' .. vehicle.plate .. ') aus der Beschlagnahme freigeben?', - centered = true, - cancel = true - }) - if alert == 'confirm' then - TriggerServerEvent('vehicleadmin:releaseFromImpound', vehicle.plate, garage) - end end end end) @@ -196,7 +164,7 @@ local function ShowPlayerVehicles(citizenid, playerName, playerJob) local options = {} for i = 1, #vehicles do local vehicle = vehicles[i] - local vehicleLabel = QBCore.Shared.Vehicles[vehicle.vehicle]?.name or vehicle.vehicle + local vehicleLabel = vehicle.name or QBCore.Shared.Vehicles[vehicle.vehicle]?.name or vehicle.vehicle -- Color coding based on state local icon = 'car' @@ -213,14 +181,22 @@ local function ShowPlayerVehicles(citizenid, playerName, playerJob) iconColor = '#ef4444' end - local description = GetStateText(vehicle.state) .. ' | 🏠 ' .. (vehicle.garage or 'Keine Garage') .. - '\n🔧 Motor: ' .. GetConditionText(vehicle.engine) .. ' | 🚗 Karosserie: ' .. GetConditionText(vehicle.body) .. - '\n' .. GetFuelText(vehicle.fuel) - - if vehicle.depotprice and vehicle.depotprice > 0 then - description = description .. '\n💰 Gebühr: ' .. vehicle.depotprice .. '€' + -- Parse mods if needed + local mods = {} + if type(vehicle.mods) == 'string' then + mods = json.decode(vehicle.mods) or {} + else + mods = vehicle.mods or {} end + local engineHealth = mods.engineHealth or 1000 + local bodyHealth = mods.bodyHealth or 1000 + local fuelLevel = mods.fuelLevel or 100 + + local description = GetStateText(vehicle.state) .. ' | 🏠 ' .. (vehicle.garage or 'Keine Garage') .. + '\n🔧 Motor: ' .. GetConditionText(engineHealth) .. ' | 🚗 Karosserie: ' .. GetConditionText(bodyHealth) .. + '\n' .. GetFuelText(fuelLevel) + table.insert(options, { title = vehicleLabel .. ' (' .. vehicle.plate .. ')', description = description, @@ -295,34 +271,7 @@ RegisterNetEvent('vehicleadmin:openMenu', function() ShowPlayerMenu() end) --- Keybind for quick access (optional) -RegisterKeyMapping('vehicleadmin', 'Öffne Fahrzeug Admin Menu', 'keyboard', '') - --- Show help text when menu is opened -RegisterNetEvent('vehicleadmin:showHelp', function() - lib.notify({ - title = 'Fahrzeugverwaltung', - description = 'Verwende /vehicleadmin oder die entsprechenden Job-Befehle um das Menu zu öffnen', - type = 'info', - duration = 5000 - }) -end) - --- Auto-refresh function for real-time updates -local function RefreshCurrentMenu() - -- This can be used to refresh the current menu if needed - -- Implementation depends on your specific needs -end - -- Export functions for other scripts exports('OpenVehicleAdmin', function() ShowPlayerMenu() end) - -exports('HasVehicleAdminPermission', function() - local hasPermission = false - QBCore.Functions.TriggerCallback('vehicleadmin:getPlayerJob', function(jobData) - hasPermission = jobData and jobData.hasPermission or false - end) - return hasPermission -end) diff --git a/resources/[carscripts]/nordi_car_admin/server.lua b/resources/[carscripts]/nordi_car_admin/server.lua index 6c73fa278..aab8d9f79 100644 --- a/resources/[carscripts]/nordi_car_admin/server.lua +++ b/resources/[carscripts]/nordi_car_admin/server.lua @@ -33,13 +33,20 @@ QBCore.Functions.CreateCallback('vehicleadmin:getPlayers', function(source, cb) end MySQL.Async.fetchAll('SELECT citizenid, charinfo FROM players ORDER BY charinfo', {}, function(result) + if not result then + cb({}) + return + end + local players = {} for i = 1, #result do local charinfo = json.decode(result[i].charinfo) - table.insert(players, { - citizenid = result[i].citizenid, - name = charinfo.firstname .. ' ' .. charinfo.lastname - }) + if charinfo and charinfo.firstname and charinfo.lastname then + table.insert(players, { + citizenid = result[i].citizenid, + name = charinfo.firstname .. ' ' .. charinfo.lastname + }) + end end cb(players) end) @@ -53,28 +60,33 @@ QBCore.Functions.CreateCallback('vehicleadmin:getPlayerVehicles', function(sourc end MySQL.Async.fetchAll('SELECT * FROM player_vehicles WHERE citizenid = ?', {citizenid}, function(result) + if not result then + cb({}) + return + end + local vehicles = {} for i = 1, #result do local vehicle = result[i] table.insert(vehicles, { - plate = vehicle.plate, - vehicle = vehicle.vehicle, - garage = vehicle.garage, - state = vehicle.state, - fuel = vehicle.fuel, - engine = vehicle.engine, - body = vehicle.body, + plate = vehicle.plate or 'UNKNOWN', + vehicle = vehicle.vehicle or 'unknown', + garage = vehicle.garage or 'none', + state = vehicle.garage == "OUT" and 0 or 1, -- Anpassung an euer Garagensystem + fuel = vehicle.fuel or 100, + engine = vehicle.engine or 1000, + body = vehicle.body or 1000, citizenid = vehicle.citizenid, - depotprice = vehicle.depotprice, - drivingdistance = vehicle.drivingdistance + name = vehicle.name or "Unbekanntes Fahrzeug", + mods = vehicle.mods or "{}" }) end cb(vehicles) end) end) --- Get all garages +-- Get all garages from your config QBCore.Functions.CreateCallback('vehicleadmin:getGarages', function(source, cb) if not HasPermission(source) then cb(false) @@ -82,12 +94,20 @@ QBCore.Functions.CreateCallback('vehicleadmin:getGarages', function(source, cb) end local garages = {} - for garageName, garageData in pairs(QBCore.Shared.Garages) do + + -- Verwende die Garagen aus eurem Config + for k, v in pairs(Config.Zonen) do table.insert(garages, { - name = garageName, - label = garageData.label or garageName + name = v.name, + label = v.name -- Ihr könnt hier auch eine Label-Property hinzufügen }) end + + -- Sortiere alphabetisch + table.sort(garages, function(a, b) + return a.name < b.name + end) + cb(garages) end) @@ -126,24 +146,40 @@ RegisterNetEvent('vehicleadmin:moveToGarage', function(plate, garage) local vehicles = GetAllVehicles() for i = 1, #vehicles do local veh = vehicles[i] - local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") - if vehPlate == plate:gsub("%s+", "") then - DeleteEntity(veh) - break + if DoesEntityExist(veh) then + local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") + if vehPlate == plate:gsub("%s+", "") then + DeleteEntity(veh) + break + end end end - -- Update database - MySQL.Async.execute('UPDATE player_vehicles SET state = ?, garage = ? WHERE plate = ?', {1, garage, plate}, function(affectedRows) + -- Update database - Anpassung an euer Garagensystem + MySQL.Async.execute('UPDATE player_vehicles SET garage = ? WHERE plate = ?', {garage, plate}, function(affectedRows) if affectedRows > 0 then + -- Entferne das Fahrzeug aus dem Parking-System, falls vorhanden + MySQL.Async.execute('DELETE FROM vehicle_parking WHERE plate = ?', {plate}) + -- Log the action local Player = QBCore.Functions.GetPlayer(src) - print(string.format('[VEHICLE ADMIN] %s (%s) moved vehicle %s to garage %s', - Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, - Player.PlayerData.job.label, - plate, - garage - )) + if Player then + print(string.format('[VEHICLE ADMIN] %s (%s) moved vehicle %s to garage %s', + Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, + Player.PlayerData.job.label, + plate, + garage + )) + + -- Log für Discord + TriggerEvent('mh_garage:log', { + type = "admin_move", + user = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, + citizenid = Player.PlayerData.citizenid, + plate = plate, + garage = garage + }) + end TriggerClientEvent('ox_lib:notify', src, { title = 'Fahrzeugverwaltung', @@ -177,21 +213,33 @@ RegisterNetEvent('vehicleadmin:deleteFromMap', function(plate) for i = 1, #vehicles do local veh = vehicles[i] - local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") - if vehPlate == plate:gsub("%s+", "") then - DeleteEntity(veh) - deleted = true - break + if DoesEntityExist(veh) then + local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") + if vehPlate == plate:gsub("%s+", "") then + DeleteEntity(veh) + deleted = true + break + end end end -- Log the action local Player = QBCore.Functions.GetPlayer(src) - print(string.format('[VEHICLE ADMIN] %s (%s) deleted vehicle %s from map', - Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, - Player.PlayerData.job.label, - plate - )) + if Player then + print(string.format('[VEHICLE ADMIN] %s (%s) deleted vehicle %s from map', + Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, + Player.PlayerData.job.label, + plate + )) + + -- Log für Discord + TriggerEvent('mh_garage:log', { + type = "admin_delete", + user = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, + citizenid = Player.PlayerData.citizenid, + plate = plate + }) + end if deleted then TriggerClientEvent('ox_lib:notify', src, { @@ -220,129 +268,84 @@ RegisterNetEvent('vehicleadmin:repairVehicle', function(plate) return end - -- Update database - MySQL.Async.execute('UPDATE player_vehicles SET engine = ?, body = ? WHERE plate = ?', {1000, 1000, plate}, function(affectedRows) - -- Also repair if vehicle is on map - local vehicles = GetAllVehicles() - for i = 1, #vehicles do - local veh = vehicles[i] - local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") - if vehPlate == plate:gsub("%s+", "") then - SetVehicleFixed(veh) - SetVehicleDeformationFixed(veh) - SetVehicleUndriveable(veh, false) - SetVehicleEngineOn(veh, true, true) - break + -- Get vehicle mods first + MySQL.Async.fetchAll('SELECT mods FROM player_vehicles WHERE plate = ?', {plate}, function(result) + if result and result[1] and result[1].mods then + local mods = json.decode(result[1].mods) + + -- Update mods with repaired values + mods.engineHealth = 1000 + mods.bodyHealth = 1000 + mods.fuelLevel = 100 + + -- Reset door and window status if they exist + if mods.doorStatus then + mods.doorStatus = {} end - end - - -- Log the action - local Player = QBCore.Functions.GetPlayer(src) - print(string.format('[VEHICLE ADMIN] %s (%s) repaired vehicle %s', - Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, - Player.PlayerData.job.label, - plate - )) - - if affectedRows > 0 then - TriggerClientEvent('ox_lib:notify', src, { - title = 'Fahrzeugverwaltung', - description = 'Fahrzeug wurde repariert', - type = 'success' - }) - else - TriggerClientEvent('ox_lib:notify', src, { - title = 'Fahrzeugverwaltung', - description = 'Fehler beim Reparieren des Fahrzeugs', - type = 'error' - }) - end - end) -end) - --- Impound vehicle (Police only) -RegisterNetEvent('vehicleadmin:impoundVehicle', function(plate) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player or Player.PlayerData.job.name ~= 'police' then - TriggerClientEvent('ox_lib:notify', src, { - title = 'Keine Berechtigung', - description = 'Nur Polizisten können Fahrzeuge beschlagnahmen', - type = 'error' - }) - return - end - - -- Delete vehicle from world if it exists - local vehicles = GetAllVehicles() - for i = 1, #vehicles do - local veh = vehicles[i] - local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") - if vehPlate == plate:gsub("%s+", "") then - DeleteEntity(veh) - break - end - end - - -- Set state to impounded (2) and move to impound garage - MySQL.Async.execute('UPDATE player_vehicles SET state = ?, garage = ?, depotprice = ? WHERE plate = ?', - {2, 'impound', 500, plate}, function(affectedRows) - if affectedRows > 0 then - print(string.format('[VEHICLE ADMIN] %s (Police) impounded vehicle %s', - Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, - plate - )) - TriggerClientEvent('ox_lib:notify', src, { - title = 'Fahrzeugverwaltung', - description = 'Fahrzeug wurde beschlagnahmt (500€ Gebühr)', - type = 'success' - }) - else - TriggerClientEvent('ox_lib:notify', src, { - title = 'Fahrzeugverwaltung', - description = 'Fehler beim Beschlagnahmen des Fahrzeugs', - type = 'error' - }) - end - end) -end) - --- Release from impound (Police/Admin only) -RegisterNetEvent('vehicleadmin:releaseFromImpound', function(plate, garage) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player or (Player.PlayerData.job.name ~= 'police' and not QBCore.Functions.HasPermission(src, 'admin')) then - TriggerClientEvent('ox_lib:notify', src, { - title = 'Keine Berechtigung', - description = 'Nur Polizisten oder Admins können Fahrzeuge freigeben', - type = 'error' - }) - return - end - - -- Set state to in garage (1) and move to specified garage - MySQL.Async.execute('UPDATE player_vehicles SET state = ?, garage = ?, depotprice = ? WHERE plate = ?', - {1, garage, 0, plate}, function(affectedRows) - if affectedRows > 0 then - print(string.format('[VEHICLE ADMIN] %s (%s) released vehicle %s from impound to garage %s', - Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, - Player.PlayerData.job.label, - plate, - garage - )) + if mods.windowStatus then + mods.windowStatus = {} + for i = 0, 7 do + mods.windowStatus[tostring(i)] = true + end + end - TriggerClientEvent('ox_lib:notify', src, { - title = 'Fahrzeugverwaltung', - description = 'Fahrzeug wurde aus der Beschlagnahme freigegeben', - type = 'success' - }) + -- Update database with repaired mods + MySQL.Async.execute('UPDATE player_vehicles SET mods = ? WHERE plate = ?', {json.encode(mods), plate}, function(affectedRows) + -- Also repair if vehicle is on map + local vehicles = GetAllVehicles() + for i = 1, #vehicles do + local veh = vehicles[i] + if DoesEntityExist(veh) then + local vehPlate = GetVehicleNumberPlateText(veh):gsub("%s+", "") + if vehPlate == plate:gsub("%s+", "") then + SetVehicleFixed(veh) + SetVehicleDeformationFixed(veh) + SetVehicleUndriveable(veh, false) + SetVehicleEngineOn(veh, true, true) + SetVehicleFuelLevel(veh, 100.0) + exports["lc_fuel"]:SetFuel(veh, 100) + break + end + end + end + + -- Log the action + local Player = QBCore.Functions.GetPlayer(src) + if Player then + print(string.format('[VEHICLE ADMIN] %s (%s) repaired vehicle %s', + Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, + Player.PlayerData.job.label, + plate + )) + + -- Log für Discord + TriggerEvent('mh_garage:log', { + type = "admin_repair", + user = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, + citizenid = Player.PlayerData.citizenid, + plate = plate + }) + end + + if affectedRows > 0 then + TriggerClientEvent('ox_lib:notify', src, { + title = 'Fahrzeugverwaltung', + description = 'Fahrzeug wurde repariert', + type = 'success' + }) + else + TriggerClientEvent('ox_lib:notify', src, { + title = 'Fahrzeugverwaltung', + description = 'Fehler beim Reparieren des Fahrzeugs', + type = 'error' + }) + end + end) else TriggerClientEvent('ox_lib:notify', src, { title = 'Fahrzeugverwaltung', - description = 'Fehler beim Freigeben des Fahrzeugs', + description = 'Fahrzeug nicht gefunden', type = 'error' }) end @@ -434,3 +437,27 @@ QBCore.Commands.Add('dealeradmin', 'Öffne Autohändler Fahrzeug Menu', {}, fals TriggerClientEvent('vehicleadmin:openMenu', src) end) + +-- Erweiterung des Log-Systems für Fahrzeugadmin +AddEventHandler('mh_garage:log', function(data) + local type = data.type + local user = data.user + local citizenid = data.citizenid + local plate = data.plate + local garage = data.garage + local Color = "purple" + local Text = "" + + if type == "admin_move" then + Text = user .. " [" .. citizenid .. "] hat Fahrzeug " .. plate .. " in Garage " .. garage .. " gestellt." + Color = "blue" + elseif type == "admin_delete" then + Text = user .. " [" .. citizenid .. "] hat Fahrzeug " .. plate .. " von der Map gelöscht." + Color = "orange" + elseif type == "admin_repair" then + Text = user .. " [" .. citizenid .. "] hat Fahrzeug " .. plate .. " repariert." + Color = "green" + end + + sendToDiscord("Fahrzeugadmin", Text, Color) +end) diff --git a/resources/[inventory]/tgiann-inventory/configs/configStashes.lua b/resources/[inventory]/tgiann-inventory/configs/configStashes.lua index 96fcada10..5ceb1cc26 100644 --- a/resources/[inventory]/tgiann-inventory/configs/configStashes.lua +++ b/resources/[inventory]/tgiann-inventory/configs/configStashes.lua @@ -164,7 +164,7 @@ config.stashes = { coords = vector3(1825.34, 3684.54, 34.4), textUiLabel = 'öffnen', name = 'sheriffarmory', -- uniq name - label = 'Aromory', + label = 'Armory', slots = 80, weight = 100000, jobs = { police = { 7, 8, 9, 10 } } -- only 9 ranks for police job diff --git a/resources/[phone]/roadphone/addons/jobcalls/config.lua b/resources/[phone]/roadphone/addons/jobcalls/config.lua index 22dacc1ab..b2d87734c 100644 --- a/resources/[phone]/roadphone/addons/jobcalls/config.lua +++ b/resources/[phone]/roadphone/addons/jobcalls/config.lua @@ -3,7 +3,7 @@ JobCalls = {} JobCalls.Positions = { { name = "Police", - coords = vector3(454.1935, -984.5326, 30.6610), + coords = vector3(1825.22, 3670.96, 34.4), --- SD vector3(1825.2719, 3670.9895, 34.4050), number = 911, needjob = "police",