forked from Simnation/Main
392 lines
14 KiB
Lua
392 lines
14 KiB
Lua
local QBCore = exports['qb-core']:GetCoreObject()
|
|
local playerDrivenVehicles = {} -- Nur Fahrzeuge die der Spieler gefahren hat
|
|
|
|
-- Funktion um zu prüfen ob Fahrzeugklasse erlaubt ist
|
|
local function IsVehicleClassAllowed(vehicle)
|
|
local vehicleClass = GetVehicleClass(vehicle)
|
|
for _, allowedClass in pairs(Config.AllowedVehicleClasses) do
|
|
if vehicleClass == allowedClass then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Stärkere Despawn-Verhinderung
|
|
local function PreventDespawn(vehicle)
|
|
if DoesEntityExist(vehicle) then
|
|
SetEntityAsMissionEntity(vehicle, true, true)
|
|
SetVehicleHasBeenOwnedByPlayer(vehicle, true)
|
|
SetVehicleNeedsToBeHotwired(vehicle, false)
|
|
SetEntityLoadCollisionFlag(vehicle, true)
|
|
|
|
-- Zusätzliche Flags
|
|
SetVehicleIsStolen(vehicle, false)
|
|
SetVehicleIsWanted(vehicle, false)
|
|
|
|
-- Verhindere dass das Fahrzeug als "abandoned" markiert wird
|
|
if DecorExistOn(vehicle, "IgnoredByQuickSave") then
|
|
DecorSetBool(vehicle, "IgnoredByQuickSave", false)
|
|
end
|
|
|
|
-- Setze Fahrzeug auf Boden
|
|
SetVehicleOnGroundProperly(vehicle)
|
|
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Funktion um Fahrzeugmods zu erhalten
|
|
local function GetVehicleMods(vehicle)
|
|
local mods = {}
|
|
|
|
-- Basis Mods
|
|
for i = 0, 49 do
|
|
mods[tostring(i)] = GetVehicleMod(vehicle, i)
|
|
end
|
|
|
|
-- Extras
|
|
mods.extras = {}
|
|
for i = 1, 12 do
|
|
if DoesExtraExist(vehicle, i) then
|
|
mods.extras[tostring(i)] = IsVehicleExtraTurnedOn(vehicle, i)
|
|
end
|
|
end
|
|
|
|
-- Farben
|
|
local primaryColor, secondaryColor = GetVehicleColours(vehicle)
|
|
local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle)
|
|
|
|
mods.colors = {
|
|
primary = primaryColor,
|
|
secondary = secondaryColor,
|
|
pearlescent = pearlescentColor,
|
|
wheels = wheelColor
|
|
}
|
|
|
|
-- Neon
|
|
mods.neon = {
|
|
left = IsVehicleNeonLightEnabled(vehicle, 0),
|
|
right = IsVehicleNeonLightEnabled(vehicle, 1),
|
|
front = IsVehicleNeonLightEnabled(vehicle, 2),
|
|
back = IsVehicleNeonLightEnabled(vehicle, 3)
|
|
}
|
|
|
|
local r, g, b = GetVehicleNeonLightsColour(vehicle)
|
|
mods.neonColor = {r = r, g = g, b = b}
|
|
|
|
return mods
|
|
end
|
|
|
|
-- Funktion um Fahrzeugmods zu setzen
|
|
local function SetVehicleMods(vehicle, mods)
|
|
if not mods then return end
|
|
|
|
-- Basis Mods
|
|
for i = 0, 49 do
|
|
if mods[tostring(i)] then
|
|
SetVehicleMod(vehicle, i, mods[tostring(i)], false)
|
|
end
|
|
end
|
|
|
|
-- Extras
|
|
if mods.extras then
|
|
for i = 1, 12 do
|
|
if mods.extras[tostring(i)] ~= nil then
|
|
SetVehicleExtra(vehicle, i, not mods.extras[tostring(i)])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Farben
|
|
if mods.colors then
|
|
SetVehicleColours(vehicle, mods.colors.primary or 0, mods.colors.secondary or 0)
|
|
SetVehicleExtraColours(vehicle, mods.colors.pearlescent or 0, mods.colors.wheels or 0)
|
|
end
|
|
|
|
-- Neon
|
|
if mods.neon then
|
|
SetVehicleNeonLightEnabled(vehicle, 0, mods.neon.left or false)
|
|
SetVehicleNeonLightEnabled(vehicle, 1, mods.neon.right or false)
|
|
SetVehicleNeonLightEnabled(vehicle, 2, mods.neon.front or false)
|
|
SetVehicleNeonLightEnabled(vehicle, 3, mods.neon.back or false)
|
|
end
|
|
|
|
if mods.neonColor then
|
|
SetVehicleNeonLightsColour(vehicle, mods.neonColor.r, mods.neonColor.g, mods.neonColor.b)
|
|
end
|
|
end
|
|
|
|
-- Event Handler für Fahrzeug betreten (nur Fahrersitz)
|
|
CreateThread(function()
|
|
local lastVehicle = 0
|
|
|
|
while true do
|
|
Wait(1000)
|
|
|
|
local playerPed = PlayerPedId()
|
|
local currentVehicle = GetVehiclePedIsIn(playerPed, false)
|
|
|
|
-- Spieler ist als Fahrer in ein Fahrzeug eingestiegen
|
|
if currentVehicle ~= 0 and currentVehicle ~= lastVehicle then
|
|
-- Prüfe ob Spieler auf Fahrersitz ist
|
|
local driver = GetPedInVehicleSeat(currentVehicle, -1)
|
|
|
|
-- Nur wenn Spieler der Fahrer ist (Seat -1)
|
|
if driver == playerPed and IsVehicleClassAllowed(currentVehicle) then
|
|
local plate = QBCore.Functions.GetPlate(currentVehicle)
|
|
playerDrivenVehicles[plate] = currentVehicle
|
|
|
|
-- Sofort starke Despawn-Verhinderung
|
|
PreventDespawn(currentVehicle)
|
|
|
|
if Config.Debug then
|
|
print(string.format("Player started driving vehicle: %s", plate))
|
|
end
|
|
end
|
|
end
|
|
|
|
lastVehicle = currentVehicle
|
|
end
|
|
end)
|
|
|
|
-- Kontinuierliche Despawn-Verhinderung für alle getrackten Fahrzeuge
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(10000) -- Alle 10 Sekunden
|
|
|
|
for plate, vehicle in pairs(playerDrivenVehicles) do
|
|
if DoesEntityExist(vehicle) then
|
|
PreventDespawn(vehicle)
|
|
if Config.Debug then
|
|
print(string.format("Refreshing despawn protection for: %s", plate))
|
|
end
|
|
else
|
|
playerDrivenVehicles[plate] = nil
|
|
if Config.Debug then
|
|
print(string.format("Player driven vehicle no longer exists: %s", plate))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Hauptloop für Fahrzeugtracking (nur für vom Spieler gefahrene Fahrzeuge)
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(Config.SaveInterval)
|
|
|
|
-- Tracke nur Fahrzeuge die der Spieler gefahren hat
|
|
for plate, vehicle in pairs(playerDrivenVehicles) do
|
|
if DoesEntityExist(vehicle) then
|
|
local vehicleCoords = GetEntityCoords(vehicle)
|
|
|
|
-- Verstärke Despawn-Schutz bei jedem Save
|
|
PreventDespawn(vehicle)
|
|
|
|
-- Speichere Fahrzeugdaten
|
|
local vehicleData = {
|
|
plate = plate,
|
|
model = GetEntityModel(vehicle),
|
|
position = {x = vehicleCoords.x, y = vehicleCoords.y, z = vehicleCoords.z},
|
|
rotation = {x = 0.0, y = 0.0, z = GetEntityHeading(vehicle)},
|
|
engineHealth = GetVehicleEngineHealth(vehicle),
|
|
bodyHealth = GetVehicleBodyHealth(vehicle),
|
|
fuel = 100,
|
|
mods = GetVehicleMods(vehicle)
|
|
}
|
|
|
|
-- Versuche Fuel zu bekommen
|
|
if GetResourceState('LegacyFuel') == 'started' then
|
|
vehicleData.fuel = exports['LegacyFuel']:GetFuel(vehicle) or 100
|
|
elseif GetResourceState('ps-fuel') == 'started' then
|
|
vehicleData.fuel = exports['ps-fuel']:GetFuel(vehicle) or 100
|
|
end
|
|
|
|
TriggerServerEvent('vehicle-persistence:server:saveVehiclePosition', vehicleData)
|
|
|
|
if Config.Debug then
|
|
print(string.format("Saving player driven vehicle: %s", plate))
|
|
end
|
|
else
|
|
-- Fahrzeug existiert nicht mehr, entferne aus Liste
|
|
playerDrivenVehicles[plate] = nil
|
|
if Config.Debug then
|
|
print(string.format("Player driven vehicle no longer exists: %s", plate))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Spawne gespeicherte Fahrzeuge
|
|
RegisterNetEvent('vehicle-persistence:client:spawnSavedVehicles', function(vehicles)
|
|
if Config.Debug then
|
|
print(string.format("Received %d vehicles to spawn", #vehicles))
|
|
end
|
|
|
|
for _, vehicleData in pairs(vehicles) do
|
|
if Config.Debug then
|
|
print(string.format("Processing vehicle: %s", vehicleData.plate))
|
|
end
|
|
|
|
local position = json.decode(vehicleData.position)
|
|
local rotation = json.decode(vehicleData.rotation)
|
|
|
|
-- Prüfe ob Fahrzeug bereits existiert
|
|
local existingVehicle = GetVehicleByPlate(vehicleData.plate)
|
|
if not existingVehicle then
|
|
CreateThread(function()
|
|
local modelHash = vehicleData.model
|
|
if type(modelHash) == "string" then
|
|
modelHash = GetHashKey(modelHash)
|
|
end
|
|
|
|
if Config.Debug then
|
|
print(string.format("Requesting model: %s (Hash: %s)", vehicleData.model, modelHash))
|
|
end
|
|
|
|
RequestModel(modelHash)
|
|
local timeout = 0
|
|
while not HasModelLoaded(modelHash) and timeout < 100 do
|
|
Wait(100)
|
|
timeout = timeout + 1
|
|
end
|
|
|
|
if HasModelLoaded(modelHash) then
|
|
if Config.Debug then
|
|
print(string.format("Model loaded, creating vehicle at: %.2f, %.2f, %.2f", position.x, position.y, position.z))
|
|
end
|
|
|
|
local vehicle = CreateVehicle(modelHash, position.x, position.y, position.z, rotation.z, true, false)
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
Wait(1000) -- Warte bis Fahrzeug vollständig geladen
|
|
|
|
-- Setze Fahrzeugdaten
|
|
SetVehicleNumberPlateText(vehicle, vehicleData.plate)
|
|
SetVehicleEngineHealth(vehicle, vehicleData.engine_health or 1000.0)
|
|
SetVehicleBodyHealth(vehicle, vehicleData.body_health or 1000.0)
|
|
|
|
-- Setze Fuel
|
|
if GetResourceState('LegacyFuel') == 'started' then
|
|
exports['LegacyFuel']:SetFuel(vehicle, vehicleData.fuel or 100)
|
|
elseif GetResourceState('ps-fuel') == 'started' then
|
|
exports['ps-fuel']:SetFuel(vehicle, vehicleData.fuel or 100)
|
|
end
|
|
|
|
-- Setze Mods
|
|
if vehicleData.mods then
|
|
local success, mods = pcall(json.decode, vehicleData.mods)
|
|
if success then
|
|
SetVehicleMods(vehicle, mods)
|
|
end
|
|
end
|
|
|
|
-- Starke Despawn-Verhinderung
|
|
PreventDespawn(vehicle)
|
|
|
|
playerDrivenVehicles[vehicleData.plate] = vehicle
|
|
|
|
if Config.Debug then
|
|
print(string.format("Successfully spawned saved vehicle: %s", vehicleData.plate))
|
|
end
|
|
else
|
|
if Config.Debug then
|
|
print(string.format("Failed to create vehicle: %s", vehicleData.plate))
|
|
end
|
|
end
|
|
|
|
SetModelAsNoLongerNeeded(modelHash)
|
|
else
|
|
if Config.Debug then
|
|
print(string.format("Failed to load model for vehicle: %s", vehicleData.plate))
|
|
end
|
|
end
|
|
end)
|
|
else
|
|
-- Fahrzeug existiert bereits, füge zu Liste hinzu
|
|
playerDrivenVehicles[vehicleData.plate] = existingVehicle
|
|
PreventDespawn(existingVehicle)
|
|
|
|
if Config.Debug then
|
|
print(string.format("Vehicle already exists: %s", vehicleData.plate))
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Hilfsfunktion um Fahrzeug anhand Kennzeichen zu finden
|
|
function GetVehicleByPlate(plate)
|
|
local vehicles = GetGamePool('CVehicle')
|
|
for _, vehicle in pairs(vehicles) do
|
|
if QBCore.Functions.GetPlate(vehicle) == plate then
|
|
return vehicle
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Lade Fahrzeuge beim Spawn
|
|
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
|
|
if Config.Debug then
|
|
print("Player loaded, waiting before loading vehicles...")
|
|
end
|
|
|
|
Wait(15000)
|
|
|
|
if Config.Debug then
|
|
print("Loading vehicles...")
|
|
end
|
|
|
|
TriggerServerEvent('vehicle-persistence:server:loadVehicles')
|
|
end)
|
|
|
|
-- Lade Fahrzeuge auch beim Resource Start
|
|
CreateThread(function()
|
|
Wait(20000)
|
|
|
|
local playerData = QBCore.Functions.GetPlayerData()
|
|
if playerData and playerData.citizenid then
|
|
if Config.Debug then
|
|
print("Resource started, loading vehicles for existing player...")
|
|
end
|
|
TriggerServerEvent('vehicle-persistence:server:loadVehicles')
|
|
end
|
|
end)
|
|
|
|
-- jg-advanced-garage Events
|
|
RegisterNetEvent('jg-advancedgarages:client:vehicle-stored', function(data)
|
|
if data and data.plate and playerDrivenVehicles[data.plate] then
|
|
playerDrivenVehicles[data.plate] = nil
|
|
if Config.Debug then
|
|
print(string.format("Vehicle stored in garage, removed from tracking: %s", data.plate))
|
|
end
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('jg-advancedgarages:client:vehicle-spawned', function(data)
|
|
if data and data.plate and playerDrivenVehicles[data.plate] then
|
|
playerDrivenVehicles[data.plate] = nil
|
|
if Config.Debug then
|
|
print(string.format("Vehicle spawned from garage, removed from tracking: %s", data.plate))
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Debug Command
|
|
RegisterCommand('loadvehicles', function()
|
|
if Config.Debug then
|
|
print("Manual vehicle load triggered...")
|
|
TriggerServerEvent('vehicle-persistence:server:loadVehicles')
|
|
end
|
|
end, false)
|
|
|
|
-- Cleanup beim Disconnect
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
if resourceName == GetCurrentResourceName() then
|
|
playerDrivenVehicles = {}
|
|
end
|
|
end)
|
|
|