2025-07-26 02:45:59 +02:00
|
|
|
local QBCore = exports['qb-core']:GetCoreObject()
|
|
|
|
local spawnedNPCs = {}
|
2025-07-26 03:22:23 +02:00
|
|
|
local activeRentalVehicles = {}
|
2025-07-26 02:45:59 +02:00
|
|
|
|
|
|
|
-- NPCs spawnen
|
|
|
|
CreateThread(function()
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Warte kurz, bis die Welt geladen ist
|
|
|
|
Wait(2000)
|
|
|
|
|
2025-07-26 02:45:59 +02:00
|
|
|
for i = 1, #Config.RentalLocations do
|
|
|
|
local location = Config.RentalLocations[i]
|
2025-07-26 03:22:23 +02:00
|
|
|
local modelHash = GetHashKey(location.npc.model)
|
2025-07-26 02:45:59 +02:00
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Modell laden
|
|
|
|
RequestModel(modelHash)
|
|
|
|
local timeout = 0
|
|
|
|
while not HasModelLoaded(modelHash) and timeout < 30 do
|
|
|
|
Wait(100)
|
|
|
|
timeout = timeout + 1
|
2025-07-26 02:45:59 +02:00
|
|
|
end
|
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
if HasModelLoaded(modelHash) then
|
|
|
|
-- NPC erstellen
|
|
|
|
local coords = location.npc.coords
|
|
|
|
local npc = CreatePed(4, modelHash, coords.x, coords.y, coords.z - 1.0, coords.w, false, false)
|
|
|
|
|
|
|
|
-- NPC-Eigenschaften setzen
|
|
|
|
FreezeEntityPosition(npc, true)
|
|
|
|
SetEntityInvincible(npc, true)
|
|
|
|
SetBlockingOfNonTemporaryEvents(npc, true)
|
|
|
|
SetPedDefaultComponentVariation(npc)
|
|
|
|
|
|
|
|
-- NPC speichern
|
|
|
|
spawnedNPCs[location.id] = npc
|
|
|
|
|
|
|
|
-- Debug-Info
|
|
|
|
print("NPC spawned at location: " .. location.name)
|
|
|
|
|
|
|
|
-- QB-Target für NPC
|
|
|
|
exports['qb-target']:AddTargetEntity(npc, {
|
|
|
|
options = {
|
|
|
|
{
|
|
|
|
type = "client",
|
|
|
|
event = "vehiclerental:client:openMenu",
|
|
|
|
icon = "fas fa-car",
|
|
|
|
label = "Fahrzeug mieten",
|
|
|
|
locationId = location.id
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type = "client",
|
|
|
|
event = "vehiclerental:client:returnVehicle",
|
|
|
|
icon = "fas fa-car-side",
|
|
|
|
label = "Fahrzeug zurückgeben",
|
|
|
|
locationId = location.id
|
|
|
|
}
|
2025-07-26 02:45:59 +02:00
|
|
|
},
|
2025-07-26 03:22:23 +02:00
|
|
|
distance = 2.0
|
|
|
|
})
|
|
|
|
else
|
|
|
|
print("Failed to load NPC model for location: " .. location.name)
|
|
|
|
end
|
2025-07-26 02:45:59 +02:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Mietmenü öffnen
|
|
|
|
RegisterNetEvent('vehiclerental:client:openMenu', function(data)
|
|
|
|
local locationId = data.locationId
|
|
|
|
local location = nil
|
|
|
|
|
|
|
|
for i = 1, #Config.RentalLocations do
|
|
|
|
if Config.RentalLocations[i].id == locationId then
|
|
|
|
location = Config.RentalLocations[i]
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not location then return end
|
|
|
|
|
|
|
|
local options = {}
|
|
|
|
for i = 1, #location.vehicles do
|
|
|
|
local vehicle = location.vehicles[i]
|
|
|
|
table.insert(options, {
|
|
|
|
title = vehicle.label,
|
|
|
|
description = '$' .. vehicle.price .. ' pro Stunde',
|
|
|
|
icon = 'car',
|
|
|
|
onSelect = function()
|
|
|
|
openRentalDialog(vehicle, location)
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
lib.registerContext({
|
|
|
|
id = 'vehicle_rental_menu',
|
|
|
|
title = location.name,
|
|
|
|
options = options
|
|
|
|
})
|
|
|
|
|
|
|
|
lib.showContext('vehicle_rental_menu')
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Mietdialog
|
|
|
|
function openRentalDialog(vehicle, location)
|
|
|
|
local input = lib.inputDialog('Fahrzeug mieten', {
|
|
|
|
{
|
|
|
|
type = 'number',
|
|
|
|
label = 'Mietdauer (Stunden)',
|
|
|
|
description = 'Maximale Mietdauer: ' .. Config.MaxRentalTime .. ' Stunden',
|
|
|
|
required = true,
|
|
|
|
min = 1,
|
|
|
|
max = Config.MaxRentalTime
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if not input or not input[1] then return end
|
|
|
|
|
|
|
|
local hours = tonumber(input[1])
|
|
|
|
if not hours or hours < 1 or hours > Config.MaxRentalTime then
|
|
|
|
QBCore.Functions.Notify('Ungültige Mietdauer!', 'error')
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local totalCost = vehicle.price * hours
|
|
|
|
local plate = GeneratePlate()
|
|
|
|
|
|
|
|
QBCore.Functions.TriggerCallback('vehiclerental:server:rentVehicle', function(success)
|
|
|
|
if success then
|
|
|
|
spawnRentalVehicle(vehicle.model, location.spawnPoint, plate)
|
|
|
|
end
|
|
|
|
end, {
|
|
|
|
vehicleModel = vehicle.model,
|
|
|
|
pricePerHour = vehicle.price,
|
|
|
|
hours = hours,
|
|
|
|
locationId = location.id,
|
|
|
|
plate = plate
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Fahrzeug spawnen
|
|
|
|
function spawnRentalVehicle(model, spawnPoint, plate)
|
|
|
|
RequestModel(model)
|
|
|
|
while not HasModelLoaded(model) do
|
|
|
|
Wait(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
local vehicle = CreateVehicle(model, spawnPoint.x, spawnPoint.y, spawnPoint.z, spawnPoint.w, true, false)
|
|
|
|
SetVehicleNumberPlateText(vehicle, plate)
|
|
|
|
SetEntityAsMissionEntity(vehicle, true, true)
|
|
|
|
TaskWarpPedIntoVehicle(PlayerPedId(), vehicle, -1)
|
|
|
|
|
2025-08-11 15:42:01 +02:00
|
|
|
-- Die Schlüssel werden jetzt serverseitig über MrNewbVehicleKeys verwaltet
|
|
|
|
|
2025-07-26 02:45:59 +02:00
|
|
|
SetModelAsNoLongerNeeded(model)
|
2025-07-26 03:03:43 +02:00
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Speichere das Fahrzeug lokal
|
|
|
|
activeRentalVehicles[plate] = vehicle
|
|
|
|
|
|
|
|
-- Speichere die initiale Position
|
|
|
|
local pos = GetEntityCoords(vehicle)
|
|
|
|
local rot = GetEntityRotation(vehicle)
|
|
|
|
TriggerServerEvent('vehiclerental:server:updatePosition', plate, pos, rot)
|
|
|
|
|
2025-08-09 11:04:25 +02:00
|
|
|
print("[VehicleRental] Neues Mietfahrzeug erstellt: " .. plate)
|
2025-07-26 02:45:59 +02:00
|
|
|
end
|
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Fahrzeug zurückgeben
|
2025-07-26 00:28:59 +02:00
|
|
|
RegisterNetEvent('vehiclerental:client:returnVehicle', function(data)
|
2025-07-26 02:34:34 +02:00
|
|
|
-- Hole alle aktiven Mietverhältnisse des Spielers
|
|
|
|
QBCore.Functions.TriggerCallback('vehiclerental:server:getPlayerRentals', function(rentals)
|
|
|
|
if not rentals or #rentals == 0 then
|
|
|
|
QBCore.Functions.Notify('Du hast keine aktiven Mietverhältnisse!', 'error')
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Erstelle Menü mit allen gemieteten Fahrzeugen
|
|
|
|
local options = {}
|
|
|
|
for i = 1, #rentals do
|
|
|
|
local rental = rentals[i]
|
|
|
|
|
|
|
|
table.insert(options, {
|
|
|
|
title = rental.vehicle_model .. " - " .. rental.vehicle_plate,
|
2025-07-26 02:42:03 +02:00
|
|
|
description = "Zurückgeben " .. rental.timeText,
|
2025-07-26 02:34:34 +02:00
|
|
|
icon = 'car',
|
|
|
|
onSelect = function()
|
|
|
|
returnSpecificVehicle(rental.vehicle_plate, data.locationId)
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
lib.registerContext({
|
|
|
|
id = 'return_vehicle_menu',
|
|
|
|
title = 'Fahrzeug zurückgeben',
|
|
|
|
options = options
|
|
|
|
})
|
|
|
|
|
|
|
|
lib.showContext('return_vehicle_menu')
|
|
|
|
end)
|
|
|
|
end)
|
2025-07-26 02:45:59 +02:00
|
|
|
|
2025-08-09 11:04:25 +02:00
|
|
|
-- Fahrzeug löschen (wie DV-Befehl)
|
|
|
|
function DeleteRentalVehicle(vehicle)
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
|
|
-- Standard FiveM-Methode zum Löschen von Fahrzeugen (wie DV-Befehl)
|
|
|
|
SetEntityAsMissionEntity(vehicle, true, true)
|
|
|
|
DeleteEntity(vehicle)
|
|
|
|
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
|
|
-- Zweiter Versuch mit DeleteVehicle
|
|
|
|
DeleteVehicle(vehicle)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Prüfe, ob das Fahrzeug wirklich gelöscht wurde
|
|
|
|
if not DoesEntityExist(vehicle) then
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
-- Letzter Versuch mit NetworkFadeOutEntity
|
|
|
|
NetworkFadeOutEntity(vehicle, true, true)
|
|
|
|
Wait(500)
|
|
|
|
DeleteEntity(vehicle)
|
|
|
|
return not DoesEntityExist(vehicle)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return true -- Fahrzeug existiert nicht, also gilt es als gelöscht
|
|
|
|
end
|
|
|
|
|
2025-07-26 02:45:59 +02:00
|
|
|
-- Spezifisches Fahrzeug zurückgeben
|
|
|
|
function returnSpecificVehicle(plate, locationId)
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Finde die Location-Daten
|
|
|
|
local location = nil
|
|
|
|
for i = 1, #Config.RentalLocations do
|
|
|
|
if Config.RentalLocations[i].id == locationId then
|
|
|
|
location = Config.RentalLocations[i]
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not location then
|
|
|
|
QBCore.Functions.Notify('Fehler beim Finden des Rückgabeorts!', 'error')
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Definiere den Rückgabeort
|
|
|
|
local returnPoint = vector3(location.returnPoint.x, location.returnPoint.y, location.returnPoint.z)
|
|
|
|
|
2025-07-26 02:45:59 +02:00
|
|
|
-- Finde das Fahrzeug in der Nähe
|
|
|
|
local vehicle = nil
|
2025-07-26 03:22:23 +02:00
|
|
|
local closestDistance = 50.0 -- Maximale Suchentfernung
|
2025-07-26 02:45:59 +02:00
|
|
|
|
|
|
|
-- Suche nach dem Fahrzeug mit dem Kennzeichen
|
|
|
|
for veh in EnumerateVehicles() do
|
|
|
|
local vehPlate = GetVehicleNumberPlateText(veh)
|
|
|
|
if string.gsub(vehPlate, "%s+", "") == string.gsub(plate, "%s+", "") then
|
|
|
|
local vehPos = GetEntityCoords(veh)
|
2025-07-26 03:22:23 +02:00
|
|
|
local distance = #(returnPoint - vehPos)
|
2025-07-26 02:45:59 +02:00
|
|
|
|
|
|
|
if distance < closestDistance then
|
|
|
|
vehicle = veh
|
|
|
|
closestDistance = distance
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not vehicle then
|
2025-07-26 03:22:23 +02:00
|
|
|
QBCore.Functions.Notify('Fahrzeug nicht in der Nähe des Rückgabeorts gefunden!', 'error')
|
2025-07-26 02:45:59 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Prüfe ob das Fahrzeug nahe genug am Rückgabeort ist
|
|
|
|
local vehPos = GetEntityCoords(vehicle)
|
|
|
|
local distance = #(returnPoint - vehPos)
|
|
|
|
|
|
|
|
if distance > Config.MaxReturnDistance then
|
|
|
|
QBCore.Functions.Notify('Das Fahrzeug muss näher am Rückgabeort sein! (Max. ' .. Config.MaxReturnDistance .. 'm)', 'error')
|
|
|
|
return
|
2025-07-26 02:45:59 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Fahrzeug zurückgeben
|
|
|
|
QBCore.Functions.TriggerCallback('vehiclerental:server:returnVehicle', function(success)
|
|
|
|
if success then
|
2025-08-09 11:04:25 +02:00
|
|
|
-- Fahrzeug mit DV-ähnlicher Methode löschen
|
|
|
|
if DeleteRentalVehicle(vehicle) then
|
|
|
|
QBCore.Functions.Notify('Fahrzeug erfolgreich zurückgegeben!', 'success')
|
|
|
|
else
|
|
|
|
QBCore.Functions.Notify('Fahrzeug zurückgegeben, aber konnte nicht vollständig gelöscht werden.', 'warning')
|
2025-08-09 10:42:53 +02:00
|
|
|
end
|
|
|
|
|
2025-08-09 11:04:25 +02:00
|
|
|
-- Aus lokaler Tracking-Liste entfernen
|
2025-07-26 03:22:23 +02:00
|
|
|
activeRentalVehicles[plate] = nil
|
2025-07-26 02:45:59 +02:00
|
|
|
end
|
|
|
|
end, plate)
|
|
|
|
end
|
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Lade alle aktiven Mietfahrzeuge
|
|
|
|
RegisterNetEvent('vehiclerental:client:loadRentals')
|
|
|
|
AddEventHandler('vehiclerental:client:loadRentals', function(rentals)
|
|
|
|
-- Lösche alle vorhandenen Mietfahrzeuge
|
|
|
|
for plate, vehicle in pairs(activeRentalVehicles) do
|
|
|
|
if DoesEntityExist(vehicle) then
|
2025-08-09 11:04:25 +02:00
|
|
|
DeleteRentalVehicle(vehicle)
|
2025-07-26 03:22:23 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
activeRentalVehicles = {}
|
|
|
|
|
|
|
|
-- Spawne alle aktiven Mietfahrzeuge
|
|
|
|
for _, rental in ipairs(rentals) do
|
|
|
|
-- Prüfe, ob das Fahrzeug dem aktuellen Spieler gehört
|
|
|
|
local playerCitizenId = QBCore.Functions.GetPlayerData().citizenid
|
|
|
|
|
|
|
|
if rental.citizenid == playerCitizenId then
|
|
|
|
-- Spawne das Fahrzeug nur, wenn es eine Position hat
|
|
|
|
if rental.posX ~= 0 or rental.posY ~= 0 or rental.posZ ~= 0 then
|
|
|
|
Citizen.CreateThread(function()
|
|
|
|
local model = rental.vehicle_model
|
|
|
|
local plate = rental.vehicle_plate
|
|
|
|
|
|
|
|
-- Lade das Modell
|
|
|
|
RequestModel(model)
|
|
|
|
while not HasModelLoaded(model) do
|
|
|
|
Citizen.Wait(10)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Spawne das Fahrzeug
|
|
|
|
local vehicle = CreateVehicle(model,
|
|
|
|
rental.posX, rental.posY, rental.posZ,
|
|
|
|
rental.rotZ or 0.0, true, false)
|
|
|
|
|
|
|
|
-- Setze Eigenschaften
|
|
|
|
SetVehicleNumberPlateText(vehicle, plate)
|
|
|
|
SetEntityAsMissionEntity(vehicle, true, true)
|
|
|
|
SetVehicleDoorsLocked(vehicle, 2) -- Abgeschlossen
|
|
|
|
|
|
|
|
-- Setze Rotation
|
|
|
|
SetEntityRotation(vehicle,
|
|
|
|
rental.rotX or 0.0,
|
|
|
|
rental.rotY or 0.0,
|
|
|
|
rental.rotZ or 0.0,
|
|
|
|
2, true)
|
|
|
|
|
2025-08-11 15:42:01 +02:00
|
|
|
-- Schlüssel werden jetzt über MrNewbVehicleKeys verwaltet
|
2025-07-26 03:22:23 +02:00
|
|
|
|
|
|
|
-- Speichere das Fahrzeug lokal
|
|
|
|
activeRentalVehicles[plate] = vehicle
|
|
|
|
|
2025-08-09 11:04:25 +02:00
|
|
|
print("[VehicleRental] Mietfahrzeug geladen: " .. plate)
|
2025-07-26 03:22:23 +02:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Fahrzeug zurückgegeben
|
|
|
|
RegisterNetEvent('vehiclerental:client:vehicleReturned')
|
|
|
|
AddEventHandler('vehiclerental:client:vehicleReturned', function(plate)
|
|
|
|
-- Lösche das Fahrzeug, wenn es existiert
|
2025-08-09 10:42:53 +02:00
|
|
|
if activeRentalVehicles[plate] then
|
|
|
|
local vehicle = activeRentalVehicles[plate]
|
2025-08-09 11:04:25 +02:00
|
|
|
DeleteRentalVehicle(vehicle)
|
2025-07-26 03:22:23 +02:00
|
|
|
activeRentalVehicles[plate] = nil
|
2025-08-09 11:04:25 +02:00
|
|
|
print("[VehicleRental] Mietfahrzeug gelöscht: " .. plate)
|
2025-08-09 10:42:53 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Suche nach dem Fahrzeug in der Welt anhand des Kennzeichens
|
|
|
|
for veh in EnumerateVehicles() do
|
|
|
|
local vehPlate = GetVehicleNumberPlateText(veh)
|
|
|
|
if string.gsub(vehPlate, "%s+", "") == string.gsub(plate, "%s+", "") then
|
2025-08-09 11:04:25 +02:00
|
|
|
DeleteRentalVehicle(veh)
|
2025-08-09 10:42:53 +02:00
|
|
|
print("[VehicleRental] Zusätzliches Mietfahrzeug mit Kennzeichen gelöscht: " .. plate)
|
|
|
|
break
|
|
|
|
end
|
2025-07-26 03:22:23 +02:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Regelmäßiges Update der Fahrzeugpositionen
|
|
|
|
Citizen.CreateThread(function()
|
|
|
|
while true do
|
|
|
|
Citizen.Wait(30000) -- Alle 30 Sekunden
|
|
|
|
|
|
|
|
-- Update alle aktiven Mietfahrzeuge
|
|
|
|
for plate, vehicle in pairs(activeRentalVehicles) do
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
|
|
local pos = GetEntityCoords(vehicle)
|
|
|
|
local rot = GetEntityRotation(vehicle)
|
|
|
|
TriggerServerEvent('vehiclerental:server:updatePosition', plate, pos, rot)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Wenn der Spieler das Fahrzeug verlässt, aktualisiere die Position
|
|
|
|
Citizen.CreateThread(function()
|
|
|
|
while true do
|
|
|
|
Citizen.Wait(500)
|
|
|
|
|
|
|
|
local playerPed = PlayerPedId()
|
|
|
|
local vehicle = GetVehiclePedIsIn(playerPed, true)
|
|
|
|
|
|
|
|
if vehicle ~= 0 then
|
|
|
|
local plate = GetVehicleNumberPlateText(vehicle)
|
|
|
|
|
|
|
|
-- Wenn es ein Mietfahrzeug ist und der Spieler es gerade verlassen hat
|
|
|
|
if activeRentalVehicles[plate] and not IsPedInVehicle(playerPed, vehicle, false) then
|
|
|
|
local pos = GetEntityCoords(vehicle)
|
|
|
|
local rot = GetEntityRotation(vehicle)
|
|
|
|
TriggerServerEvent('vehiclerental:server:updatePosition', plate, pos, rot)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2025-08-09 10:42:53 +02:00
|
|
|
-- Regelmäßige Überprüfung auf "Ghost"-Mietfahrzeuge
|
|
|
|
Citizen.CreateThread(function()
|
|
|
|
while true do
|
|
|
|
Citizen.Wait(300000) -- Alle 5 Minuten
|
|
|
|
|
|
|
|
-- Hole alle aktiven Mietverhältnisse des Spielers
|
|
|
|
QBCore.Functions.TriggerCallback('vehiclerental:server:getPlayerRentals', function(rentals)
|
|
|
|
if not rentals or #rentals == 0 then return end
|
|
|
|
|
|
|
|
local playerPos = GetEntityCoords(PlayerPedId())
|
|
|
|
local plateList = {}
|
|
|
|
|
|
|
|
-- Erstelle Liste der gemieteten Kennzeichen
|
|
|
|
for _, rental in ipairs(rentals) do
|
|
|
|
table.insert(plateList, rental.vehicle_plate)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Suche nach allen Fahrzeugen in der Nähe
|
|
|
|
for veh in EnumerateVehicles() do
|
|
|
|
local vehPlate = GetVehicleNumberPlateText(veh)
|
|
|
|
local vehPos = GetEntityCoords(veh)
|
|
|
|
|
|
|
|
-- Wenn das Fahrzeug nicht in der Liste der gemieteten Fahrzeuge ist und ein RENT-Kennzeichen hat
|
|
|
|
if string.find(vehPlate, "RENT") and not activeRentalVehicles[vehPlate] then
|
|
|
|
local isRented = false
|
|
|
|
|
|
|
|
-- Prüfe, ob es in der Liste der gemieteten Fahrzeuge ist
|
|
|
|
for _, plate in ipairs(plateList) do
|
|
|
|
if string.gsub(vehPlate, "%s+", "") == string.gsub(plate, "%s+", "") then
|
|
|
|
isRented = true
|
|
|
|
activeRentalVehicles[plate] = veh -- Aktualisiere die lokale Tracking-Liste
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Wenn es nicht gemietet ist, aber ein RENT-Kennzeichen hat, lösche es
|
|
|
|
if not isRented and #(playerPos - vehPos) > 100.0 then
|
|
|
|
print("[VehicleRental] Lösche Ghost-Mietfahrzeug: " .. vehPlate)
|
2025-08-09 11:04:25 +02:00
|
|
|
DeleteRentalVehicle(veh)
|
2025-08-09 10:42:53 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2025-07-26 02:45:59 +02:00
|
|
|
-- Fahrzeug-Enumerator
|
|
|
|
function EnumerateVehicles()
|
|
|
|
return coroutine.wrap(function()
|
|
|
|
local handle, vehicle = FindFirstVehicle()
|
|
|
|
local success
|
|
|
|
repeat
|
|
|
|
coroutine.yield(vehicle)
|
|
|
|
success, vehicle = FindNextVehicle(handle)
|
|
|
|
until not success
|
|
|
|
EndFindVehicle(handle)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
-- Kennzeichen generieren (RENT + 4 zufällige Zeichen)
|
2025-07-26 02:45:59 +02:00
|
|
|
function GeneratePlate()
|
|
|
|
local plate = "RENT"
|
|
|
|
for i = 1, 4 do
|
|
|
|
if math.random(1, 2) == 1 then
|
|
|
|
plate = plate .. string.char(math.random(65, 90)) -- A-Z
|
|
|
|
else
|
|
|
|
plate = plate .. tostring(math.random(0, 9)) -- 0-9
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return plate
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Cleanup beim Ressourcen-Stop
|
|
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
|
|
if GetCurrentResourceName() ~= resourceName then return end
|
|
|
|
|
|
|
|
for _, npc in pairs(spawnedNPCs) do
|
|
|
|
if DoesEntityExist(npc) then
|
|
|
|
DeleteEntity(npc)
|
|
|
|
end
|
|
|
|
end
|
2025-07-26 03:03:43 +02:00
|
|
|
|
2025-07-26 03:22:23 +02:00
|
|
|
for plate, vehicle in pairs(activeRentalVehicles) do
|
|
|
|
if DoesEntityExist(vehicle) then
|
2025-08-09 11:04:25 +02:00
|
|
|
DeleteRentalVehicle(vehicle)
|
2025-07-26 03:22:23 +02:00
|
|
|
end
|
2025-07-26 03:03:43 +02:00
|
|
|
end
|
|
|
|
end)
|
2025-07-26 03:54:08 +02:00
|
|
|
|
|
|
|
-- Überprüfe Mietfahrzeug-Schlüssel beim Laden des Spielers
|
|
|
|
RegisterNetEvent('QBCore:Client:OnPlayerLoaded')
|
|
|
|
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
|
|
|
|
TriggerServerEvent('vehiclerental:server:checkRentalKeys')
|
|
|
|
end)
|