diff --git a/resources/[carscripts]/nordi_seats/client/main.lua b/resources/[carscripts]/nordi_seats/client/main.lua index a0bcb3351..ce85eb12e 100644 --- a/resources/[carscripts]/nordi_seats/client/main.lua +++ b/resources/[carscripts]/nordi_seats/client/main.lua @@ -50,6 +50,52 @@ local function isBoat(vehicle) return boatClasses[vehicleClass] or false end +-- Lade verankerte Boote vom Server +local function loadAnchoredBoats() + QBCore.Functions.TriggerCallback('nordi_seats:server:getAnchoredBoats', function(serverAnchors) + if serverAnchors then + for plate, anchorData in pairs(serverAnchors) do + -- Finde Fahrzeug mit dieser Kennzeichen + local vehicles = GetGamePool('CVehicle') + for _, vehicle in pairs(vehicles) do + if DoesEntityExist(vehicle) then + local vehicleProps = QBCore.Functions.GetVehicleProperties(vehicle) + if vehicleProps.plate == plate and isBoat(vehicle) then + -- Setze Boot an gespeicherte Position + SetEntityCoords(vehicle, anchorData.coords.x, anchorData.coords.y, anchorData.coords.z) + SetEntityHeading(vehicle, anchorData.heading) + FreezeEntityPosition(vehicle, true) + SetEntityInvincible(vehicle, true) + + local vehicleNetId = NetworkGetNetworkIdFromEntity(vehicle) + anchoredBoats[vehicleNetId] = anchorData + + print("^2[Anker]^7 Boot mit Kennzeichen " .. plate .. " wurde verankert geladen.") + break + end + end + end + end + end + end) +end + +-- Speichere Anker auf Server +local function saveAnchorToServer(vehicle, anchorData) + local vehicleProps = QBCore.Functions.GetVehicleProperties(vehicle) + if vehicleProps.plate then + TriggerServerEvent('nordi_seats:server:saveAnchor', vehicleProps.plate, anchorData) + end +end + +-- Entferne Anker vom Server +local function removeAnchorFromServer(vehicle) + local vehicleProps = QBCore.Functions.GetVehicleProperties(vehicle) + if vehicleProps.plate then + TriggerServerEvent('nordi_seats:server:removeAnchor', vehicleProps.plate) + end +end + -- Anker setzen/entfernen local function toggleAnchor(vehicle) if not isBoat(vehicle) then @@ -65,6 +111,9 @@ local function toggleAnchor(vehicle) SetEntityInvincible(vehicle, false) anchoredBoats[vehicleNetId] = nil + -- Entferne vom Server + removeAnchorFromServer(vehicle) + QBCore.Functions.Notify('⚓ Anker gelichtet! Das Boot kann wieder bewegt werden.', 'success') -- Effekt: Kurzes Wackeln beim Anker lichten @@ -85,17 +134,22 @@ local function toggleAnchor(vehicle) FreezeEntityPosition(vehicle, true) SetEntityInvincible(vehicle, true) - anchoredBoats[vehicleNetId] = { + local anchorData = { coords = coords, heading = heading, time = GetGameTimer() } + anchoredBoats[vehicleNetId] = anchorData + + -- Speichere auf Server + saveAnchorToServer(vehicle, anchorData) + QBCore.Functions.Notify('⚓ Anker geworfen! Das Boot ist jetzt verankert.', 'success') -- Anker-Wurf Effekt CreateThread(function() - -- Spiele Anker-Sound (falls vorhanden) + -- Spiele Anker-Sound PlaySoundFromEntity(-1, "CHECKPOINT_PERFECT", vehicle, "HUD_MINI_GAME_SOUNDSET", 0, 0) -- Kleine Wellen-Animation @@ -103,7 +157,6 @@ local function toggleAnchor(vehicle) local coords = GetEntityCoords(vehicle) local waterHeight = GetWaterHeight(coords.x, coords.y, coords.z) if waterHeight then - -- Erstelle Wasser-Effekt um das Boot local effect = "ent_sht_water" RequestNamedPtfxAsset(effect) while not HasNamedPtfxAssetLoaded(effect) do @@ -129,6 +182,51 @@ local function isBoatAnchored(vehicle) return anchoredBoats[vehicleNetId] ~= nil end +-- Überwache gespawnte Fahrzeuge für Anker-Wiederherstellung +CreateThread(function() + local checkedVehicles = {} + + while true do + local vehicles = GetGamePool('CVehicle') + + for _, vehicle in pairs(vehicles) do + if DoesEntityExist(vehicle) and not checkedVehicles[vehicle] then + checkedVehicles[vehicle] = true + + if isBoat(vehicle) then + -- Prüfe ob dieses Boot einen gespeicherten Anker hat + local vehicleProps = QBCore.Functions.GetVehicleProperties(vehicle) + if vehicleProps.plate then + QBCore.Functions.TriggerCallback('nordi_seats:server:getAnchorByPlate', function(anchorData) + if anchorData then + -- Setze Boot an gespeicherte Position + SetEntityCoords(vehicle, anchorData.coords.x, anchorData.coords.y, anchorData.coords.z) + SetEntityHeading(vehicle, anchorData.heading) + FreezeEntityPosition(vehicle, true) + SetEntityInvincible(vehicle, true) + + local vehicleNetId = NetworkGetNetworkIdFromEntity(vehicle) + anchoredBoats[vehicleNetId] = anchorData + + print("^2[Anker]^7 Boot " .. vehicleProps.plate .. " automatisch verankert.") + end + end, vehicleProps.plate) + end + end + end + end + + -- Cleanup nicht mehr existierende Fahrzeuge + for vehicle, _ in pairs(checkedVehicles) do + if not DoesEntityExist(vehicle) then + checkedVehicles[vehicle] = nil + end + end + + Wait(5000) -- Prüfe alle 5 Sekunden + end +end) + -- Funktion um verfügbare Sitze zu bekommen local function getAvailableSeats(vehicle) local seats = {} @@ -553,10 +651,16 @@ RegisterNetEvent('vehicle:openSeatMenu', function(data) end end) +-- Lade Anker beim Start +CreateThread(function() + Wait(2000) -- Warte bis QBCore geladen ist + loadAnchoredBoats() +end) + -- Cleanup verankerte Boote bei Resource Stop AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then - -- Alle verankerten Boote wieder freigeben + -- Alle verankerten Boote wieder freigeben (aber nicht vom Server löschen) for vehicleNetId, _ in pairs(anchoredBoats) do local vehicle = NetworkGetEntityFromNetworkId(vehicleNetId) if DoesEntityExist(vehicle) then @@ -606,5 +710,5 @@ RegisterCommand('anchor', function() end, false) -- Keybinds registrieren -RegisterKeyMapping('seats', 'Sitzplatz Menü öffnen', 'keyboard', 'F6') +RegisterKeyMapping('seats', 'Sitzplatz Menü öffnen', 'keyboard', 'F1') RegisterKeyMapping('anchor', 'Anker werfen/lichten', 'keyboard', 'H') diff --git a/resources/[carscripts]/nordi_seats/server/main.lua b/resources/[carscripts]/nordi_seats/server/main.lua new file mode 100644 index 000000000..4f59cdf38 --- /dev/null +++ b/resources/[carscripts]/nordi_seats/server/main.lua @@ -0,0 +1,74 @@ +local QBCore = exports['qb-core']:GetCoreObject() +local anchoredBoats = {} -- Gespeicherte Anker {plate = anchorData} + +-- Lade Anker aus Datenbank +local function loadAnchorsFromDB() + local result = MySQL.query.await('SELECT * FROM anchored_boats') + if result then + for _, row in pairs(result) do + anchoredBoats[row.plate] = { + coords = json.decode(row.coords), + heading = row.heading, + time = row.timestamp + } + end + print("^2[Anker]^7 " .. #result .. " verankerte Boote aus der Datenbank geladen.") + end +end + +-- Speichere Anker in Datenbank +local function saveAnchorToDB(plate, anchorData) + MySQL.insert('INSERT INTO anchored_boats (plate, coords, heading, timestamp) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE coords = VALUES(coords), heading = VALUES(heading), timestamp = VALUES(timestamp)', { + plate, + json.encode(anchorData.coords), + anchorData.heading, + anchorData.time or os.time() + }) +end + +-- Entferne Anker aus Datenbank +local function removeAnchorFromDB(plate) + MySQL.execute('DELETE FROM anchored_boats WHERE plate = ?', {plate}) +end + +-- Events +RegisterNetEvent('nordi_seats:server:saveAnchor', function(plate, anchorData) + local src = source + if not plate or not anchorData then return end + + anchoredBoats[plate] = anchorData + saveAnchorToDB(plate, anchorData) + + print("^2[Anker]^7 Boot " .. plate .. " wurde verankert (Spieler: " .. src .. ")") +end) + +RegisterNetEvent('nordi_seats:server:removeAnchor', function(plate) + local src = source + if not plate then return end + + anchoredBoats[plate] = nil + removeAnchorFromDB(plate) + + print("^2[Anker]^7 Boot " .. plate .. " Anker wurde gelichtet (Spieler: " .. src .. ")") +end) + +-- Callbacks +QBCore.Functions.CreateCallback('nordi_seats:server:getAnchoredBoats', function(source, cb) + cb(anchoredBoats) +end) + +QBCore.Functions.CreateCallback('nordi_seats:server:getAnchorByPlate', function(source, cb, plate) + cb(anchoredBoats[plate]) +end) + +-- Lade Anker beim Start +CreateThread(function() + loadAnchorsFromDB() +end) + +-- Cleanup bei Resource Stop +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() == resourceName then + print("^3[Anker]^7 Resource gestoppt. Anker bleiben in der Datenbank gespeichert.") + end +end)