local QBCore = exports['qb-core']:GetCoreObject() local stationVehicles = {} local stationBlips = {} print("^2[TAXI STATIONS DEBUG]^7 Stations script loaded") -- Taxi Stationen initialisieren CreateThread(function() Wait(5000) -- Längere Wartezeit, um sicherzustellen, dass alle Ressourcen geladen sind print("^2[TAXI STATIONS DEBUG]^7 Initializing taxi stations...") InitializeTaxiStations() -- Regelmäßige Überprüfung und Wiederherstellung der Stationen CreateThread(function() while true do Wait(60000) -- Alle 60 Sekunden prüfen CheckAndRestoreStationVehicles() end end) end) function InitializeTaxiStations() print("^2[TAXI STATIONS DEBUG]^7 InitializeTaxiStations started") if not Config.TaxiStations then print("^1[TAXI STATIONS DEBUG]^7 Config.TaxiStations not found!") return end print("^2[TAXI STATIONS DEBUG]^7 Found " .. #Config.TaxiStations .. " stations") -- Zuerst alle bestehenden Fahrzeuge und Blips entfernen CleanupExistingStations() -- Dann neue erstellen for stationId, station in pairs(Config.TaxiStations) do print("^2[TAXI STATIONS DEBUG]^7 Processing station " .. stationId .. ": " .. station.name) -- Blip für Station erstellen local blip = AddBlipForCoord(station.blipCoords.x, station.blipCoords.y, station.blipCoords.z) SetBlipSprite(blip, 198) SetBlipColour(blip, 5) SetBlipScale(blip, 0.8) SetBlipAsShortRange(blip, true) BeginTextCommandSetBlipName("STRING") AddTextComponentString(station.name) EndTextCommandSetBlipName(blip) stationBlips[stationId] = blip print("^2[TAXI STATIONS DEBUG]^7 Blip created for station " .. stationId) -- Fahrzeuge an Station spawnen stationVehicles[stationId] = {} -- Verzögertes Spawnen der Fahrzeuge, um Ressourcenkonflikte zu vermeiden CreateThread(function() for vehicleId, vehicleData in pairs(station.vehicles) do -- Kleine Verzögerung zwischen jedem Fahrzeug Wait(500) print("^2[TAXI STATIONS DEBUG]^7 Spawning vehicle " .. vehicleId .. " (" .. vehicleData.model .. ") at station " .. stationId) SpawnStationVehicle(stationId, vehicleId, vehicleData) end end) end print("^2[TAXI STATIONS DEBUG]^7 All stations initialization started") end function CleanupExistingStations() print("^2[TAXI STATIONS DEBUG]^7 Cleaning up existing stations...") -- Alle bestehenden Fahrzeuge löschen for stationId, vehicles in pairs(stationVehicles) do for vehicleId, vehicleInfo in pairs(vehicles) do if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity) DeleteEntity(vehicleInfo.entity) end if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then DeleteEntity(vehicleInfo.driver) end end end -- Alle Blips entfernen for _, blip in pairs(stationBlips) do RemoveBlip(blip) end -- Variablen zurücksetzen stationVehicles = {} stationBlips = {} print("^2[TAXI STATIONS DEBUG]^7 Cleanup completed") end function SpawnStationVehicle(stationId, vehicleId, vehicleData) print("^2[TAXI STATIONS DEBUG]^7 SpawnStationVehicle: " .. stationId .. "/" .. vehicleId) -- Prüfen ob bereits ein Fahrzeug für diese Position existiert if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] and stationVehicles[stationId][vehicleId].entity and DoesEntityExist(stationVehicles[stationId][vehicleId].entity) then print("^3[TAXI STATIONS DEBUG]^7 Vehicle already exists for this position, removing it first") exports['qb-target']:RemoveTargetEntity(stationVehicles[stationId][vehicleId].entity) DeleteEntity(stationVehicles[stationId][vehicleId].entity) end -- Prüfen ob die Position frei ist local clearArea = true local vehicles = GetGamePool('CVehicle') for _, vehicle in ipairs(vehicles) do local vehCoords = GetEntityCoords(vehicle) if #(vector3(vehicleData.coords.x, vehicleData.coords.y, vehicleData.coords.z) - vehCoords) < 3.0 then clearArea = false print("^3[TAXI STATIONS DEBUG]^7 Position blocked by another vehicle, will retry later") -- Nach 30 Sekunden erneut versuchen SetTimeout(30000, function() SpawnStationVehicle(stationId, vehicleId, vehicleData) end) return end end CreateThread(function() local vehicleHash = GetHashKey(vehicleData.model) print("^2[TAXI STATIONS DEBUG]^7 Vehicle hash: " .. vehicleHash) RequestModel(vehicleHash) local timeout = GetGameTimer() + 10000 while not HasModelLoaded(vehicleHash) and GetGameTimer() < timeout do print("^3[TAXI STATIONS DEBUG]^7 Waiting for model " .. vehicleData.model .. " to load...") Wait(100) end if not HasModelLoaded(vehicleHash) then print("^1[TAXI STATIONS DEBUG]^7 Failed to load model: " .. vehicleData.model) -- Nach 30 Sekunden erneut versuchen SetTimeout(30000, function() SpawnStationVehicle(stationId, vehicleId, vehicleData) end) return end local vehicle = CreateVehicle( vehicleHash, vehicleData.coords.x, vehicleData.coords.y, vehicleData.coords.z, vehicleData.coords.w, false, false ) if not DoesEntityExist(vehicle) then print("^1[TAXI STATIONS DEBUG]^7 Failed to create vehicle!") -- Nach 30 Sekunden erneut versuchen SetTimeout(30000, function() SpawnStationVehicle(stationId, vehicleId, vehicleData) end) return end print("^2[TAXI STATIONS DEBUG]^7 Vehicle created: " .. vehicle) -- Fahrzeug konfigurieren SetEntityAsMissionEntity(vehicle, true, true) SetVehicleOnGroundProperly(vehicle) SetVehicleDoorsLocked(vehicle, 2) -- Locked SetVehicleEngineOn(vehicle, false, true, false) -- Verbesserte Persistenz SetEntityInvincible(vehicle, true) SetVehicleCanBeVisiblyDamaged(vehicle, false) SetEntityProofs(vehicle, true, true, true, true, true, true, true, true) SetVehicleExplodesOnHighExplosionDamage(vehicle, false) SetVehicleHasBeenOwnedByPlayer(vehicle, true) SetVehicleIsConsideredByPlayer(vehicle, true) -- Fahrzeug-Info speichern if not stationVehicles[stationId] then stationVehicles[stationId] = {} end stationVehicles[stationId][vehicleId] = { entity = vehicle, data = vehicleData, occupied = false } print("^2[TAXI STATIONS DEBUG]^7 Adding qb-target for vehicle " .. vehicle) -- qb-target für Fahrzeug hinzufügen exports['qb-target']:AddTargetEntity(vehicle, { options = { { type = "client", event = "taxi:enterStationVehicle", icon = "fas fa-taxi", label = "Taxi nehmen ($" .. vehicleData.pricePerKm .. "/km)", stationId = stationId, vehicleId = vehicleId } }, distance = 3.0 }) print("^2[TAXI STATIONS DEBUG]^7 qb-target added for vehicle " .. vehicle) SetModelAsNoLongerNeeded(vehicleHash) end) end function CheckAndRestoreStationVehicles() print("^2[TAXI STATIONS DEBUG]^7 Checking station vehicles...") local restoredCount = 0 for stationId, station in pairs(Config.TaxiStations) do if not stationVehicles[stationId] then stationVehicles[stationId] = {} end for vehicleId, vehicleData in pairs(station.vehicles) do local shouldSpawn = false -- Prüfen ob das Fahrzeug existiert if not stationVehicles[stationId][vehicleId] then print("^3[TAXI STATIONS DEBUG]^7 Vehicle data missing for station " .. stationId .. ", vehicle " .. vehicleId) shouldSpawn = true elseif not stationVehicles[stationId][vehicleId].entity then print("^3[TAXI STATIONS DEBUG]^7 Vehicle entity missing for station " .. stationId .. ", vehicle " .. vehicleId) shouldSpawn = true elseif not DoesEntityExist(stationVehicles[stationId][vehicleId].entity) then print("^3[TAXI STATIONS DEBUG]^7 Vehicle entity doesn't exist for station " .. stationId .. ", vehicle " .. vehicleId) shouldSpawn = true elseif stationVehicles[stationId][vehicleId].occupied then -- Fahrzeug ist besetzt, nicht neu spawnen print("^2[TAXI STATIONS DEBUG]^7 Vehicle at station " .. stationId .. ", vehicle " .. vehicleId .. " is occupied") shouldSpawn = false end if shouldSpawn then print("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station " .. stationId .. ", vehicle " .. vehicleId) SpawnStationVehicle(stationId, vehicleId, vehicleData) restoredCount = restoredCount + 1 -- Kleine Verzögerung zwischen Spawns Wait(500) end end end if restoredCount > 0 then print("^2[TAXI STATIONS DEBUG]^7 Restored " .. restoredCount .. " station vehicles") else print("^2[TAXI STATIONS DEBUG]^7 All station vehicles are present") end end -- Hilfsfunktion um Spieler-Sitz zu ermitteln function GetPlayerVehicleSeat(ped, vehicle) if not IsPedInVehicle(ped, vehicle, false) then return nil end -- Alle möglichen Sitze prüfen for seat = -1, 7 do -- -1 = Fahrer, 0 = Beifahrer, 1+ = Hintersitze if GetPedInVehicleSeat(vehicle, seat) == ped then return seat end end return nil end -- Event für Einsteigen in Station-Taxi RegisterNetEvent('taxi:enterStationVehicle', function(data) print("^2[TAXI STATIONS DEBUG]^7 Player trying to enter station vehicle") print("^2[TAXI STATIONS DEBUG]^7 Data: " .. json.encode(data)) local stationId = data.stationId local vehicleId = data.vehicleId if not stationVehicles[stationId] or not stationVehicles[stationId][vehicleId] then print("^1[TAXI STATIONS DEBUG]^7 Vehicle not found in data") lib.notify({ title = 'Taxi Service', description = 'Dieses Taxi ist nicht verfügbar', type = 'error' }) return end local vehicleInfo = stationVehicles[stationId][vehicleId] if vehicleInfo.occupied then print("^1[TAXI STATIONS DEBUG]^7 Vehicle already occupied") lib.notify({ title = 'Taxi Service', description = 'Dieses Taxi ist bereits besetzt', type = 'error' }) return end print("^2[TAXI STATIONS DEBUG]^7 Entering vehicle...") -- Spieler ins Fahrzeug setzen local playerPed = PlayerPedId() local vehicle = vehicleInfo.entity -- Türen entsperren SetVehicleDoorsLocked(vehicle, 1) -- Info-Text anzeigen während Fahrer geladen wird print("^2[TAXI STATIONS DEBUG]^7 Showing driver loading text...") lib.showTextUI('🚕 Warte an der Station - Fahrer wird geladen...', { position = "top-center", icon = 'taxi', style = { borderRadius = 10, backgroundColor = '#48BB78', color = 'white' } }) -- Kurz warten damit der Text sichtbar wird Wait(1000) -- Verschiedene Fahrer-Models versuchen local driverModels = { "A_C_Chimp", -- Taxi Driver (erste Wahl) "a_m_y_business_01", -- Business Male "a_m_m_business_01", -- Business Male 2 "mp_m_freemode_01", -- Male Freemode "a_m_y_downtown_01", -- Downtown Male "a_m_m_farmer_01", -- Farmer "a_m_y_hipster_01", -- Hipster "a_m_y_beach_01" -- Beach Guy } local driver = nil local driverHash = nil for i, modelName in pairs(driverModels) do print("^2[TAXI STATIONS DEBUG]^7 Trying driver model " .. i .. ": " .. modelName) driverHash = GetHashKey(modelName) -- Text während Model-Loading aktualisieren lib.showTextUI('🚕 Lade Fahrer-Model (' .. i .. '/' .. #driverModels .. '): ' .. modelName .. '...', { position = "top-center", icon = 'taxi', style = { borderRadius = 10, backgroundColor = '#48BB78', color = 'white' } }) RequestModel(driverHash) local timeout = GetGameTimer() + 8000 -- Längere Wartezeit local attempts = 0 while not HasModelLoaded(driverHash) and GetGameTimer() < timeout do attempts = attempts + 1 if attempts % 10 == 0 then print("^3[TAXI STATIONS DEBUG]^7 Still waiting for model " .. modelName .. " (attempt " .. attempts .. ")") end Wait(100) end if HasModelLoaded(driverHash) then print("^2[TAXI STATIONS DEBUG]^7 Driver model " .. modelName .. " loaded successfully") -- Text aktualisieren lib.showTextUI('🚕 Erstelle Fahrer...', { position = "top-center", icon = 'taxi', style = { borderRadius = 10, backgroundColor = '#48BB78', color = 'white' } }) driver = CreatePedInsideVehicle(vehicle, 26, driverHash, -1, true, false) if DoesEntityExist(driver) then print("^2[TAXI STATIONS DEBUG]^7 Driver created successfully: " .. driver) break else print("^1[TAXI STATIONS DEBUG]^7 Failed to create driver with model: " .. modelName) SetModelAsNoLongerNeeded(driverHash) end else print("^1[TAXI STATIONS DEBUG]^7 Failed to load driver model: " .. modelName) SetModelAsNoLongerNeeded(driverHash) end Wait(500) -- Kurze Pause zwischen Versuchen end -- Fallback: Notfall-Fahrer erstellen if not driver or not DoesEntityExist(driver) then print("^3[TAXI STATIONS DEBUG]^7 Using emergency fallback driver creation...") lib.showTextUI('🚕 Erstelle Notfall-Fahrer...', { position = "top-center", icon = 'taxi', style = { borderRadius = 10, backgroundColor = '#FFA500', color = 'white' } }) -- Notfall-Fallback mit Hash-Werten local emergencyModels = { `mp_m_freemode_01`, `a_m_y_hipster_01`, `a_m_m_farmer_01`, `a_m_y_beach_01` } for _, hash in pairs(emergencyModels) do RequestModel(hash) local timeout = GetGameTimer() + 5000 while not HasModelLoaded(hash) and GetGameTimer() < timeout do Wait(50) end if HasModelLoaded(hash) then driver = CreatePedInsideVehicle(vehicle, 26, hash, -1, true, false) if DoesEntityExist(driver) then print("^2[TAXI STATIONS DEBUG]^7 Emergency driver created") driverHash = hash break end SetModelAsNoLongerNeeded(hash) end end Wait(1000) end -- Wenn immer noch kein Fahrer, ohne Fahrer fortfahren if not driver or not DoesEntityExist(driver) then print("^1[TAXI STATIONS DEBUG]^7 Could not create any driver, continuing without driver") lib.showTextUI('❌ Kein Fahrer verfügbar - Du kannst selbst fahren', { position = "top-center", icon = 'exclamation-triangle', style = { borderRadius = 10, backgroundColor = '#FF6B6B', color = 'white' } }) Wait(3000) -- 3 Sekunden anzeigen lib.hideTextUI() driver = nil lib.notify({ title = 'Taxi Service', description = 'Kein Fahrer verfügbar - Du kannst das Taxi selbst fahren', type = 'warning' }) else -- Fahrer erfolgreich erstellt lib.showTextUI('✅ Fahrer bereit - Steige hinten ein!', { position = "top-center", icon = 'check-circle', style = { borderRadius = 10, backgroundColor = '#48BB78', color = 'white' } }) Wait(2000) -- 2 Sekunden anzeigen -- Fahrer konfigurieren SetEntityAsMissionEntity(driver, true, true) SetPedFleeAttributes(driver, 0, 0) SetPedCombatAttributes(driver, 17, 1) SetPedSeeingRange(driver, 0.0) SetPedHearingRange(driver, 0.0) SetPedAlertness(driver, 0) SetPedKeepTask(driver, true) SetBlockingOfNonTemporaryEvents(driver, true) -- Verbesserte Fahrer-Einstellungen SetDriverAbility(driver, 1.0) -- Maximale Fahrfähigkeit SetDriverAggressiveness(driver, 0.0) -- Minimale Aggressivität -- Fahrer-Outfit (nur wenn es ein anpassbarer Ped ist) if driverHash == GetHashKey("mp_m_freemode_01") or driverHash == GetHashKey("mp_f_freemode_01") then print("^2[TAXI STATIONS DEBUG]^7 Setting driver outfit...") -- Basis-Outfit für Taxi-Fahrer SetPedComponentVariation(driver, 8, 15, 0, 0) -- Undershirt SetPedComponentVariation(driver, 11, 28, 0, 0) -- Jacket SetPedComponentVariation(driver, 4, 10, 0, 0) -- Pants SetPedComponentVariation(driver, 6, 10, 0, 0) -- Shoes SetPedComponentVariation(driver, 1, 0, 0, 0) -- Mask SetPedComponentVariation(driver, 3, 0, 0, 0) -- Arms SetPedComponentVariation(driver, 5, 0, 0, 0) -- Bag SetPedComponentVariation(driver, 7, 0, 0, 0) -- Tie SetPedComponentVariation(driver, 9, 0, 0, 0) -- Body Armor SetPedComponentVariation(driver, 10, 0, 0, 0) -- Decals -- Zufällige Gesichtsmerkmale SetPedHeadBlendData(driver, math.random(0, 20), math.random(0, 20), 0, math.random(0, 20), math.random(0, 20), 0, 0.5, 0.5, 0.0, false) end lib.notify({ title = 'Taxi Service', description = 'Fahrer bereit - Steige hinten ein', type = 'success' }) end -- Spieler HINTEN einsteigen lassen local seatIndex = 1 -- Hinten links als Standard -- Prüfen welche Hintersitze verfügbar sind local availableSeats = {} for i = 1, 3 do -- Sitze 1, 2, 3 (hinten links, hinten mitte, hinten rechts) if IsVehicleSeatFree(vehicle, i) then table.insert(availableSeats, i) end end -- Ersten verfügbaren Hintersitz wählen if #availableSeats > 0 then seatIndex = availableSeats[1] print("^2[TAXI STATIONS DEBUG]^7 Using rear seat: " .. seatIndex) else -- Fallback: Beifahrersitz seatIndex = 0 print("^3[TAXI STATIONS DEBUG]^7 No rear seats available, using passenger seat") end -- Spieler in den gewählten Sitz einsteigen lassen TaskEnterVehicle(playerPed, vehicle, 10000, seatIndex, 1.0, 1, 0) -- Info-Text während Einsteigen lib.showTextUI('🚕 Steige ins Taxi ein...', { position = "top-center", icon = 'car-side', style = { borderRadius = 10, backgroundColor = '#4299E1', color = 'white' } }) -- Warten bis Spieler eingestiegen ist CreateThread(function() local enterTimeout = GetGameTimer() + 15000 -- Längere Zeit für Einsteigen local hasEntered = false while GetGameTimer() < enterTimeout and not hasEntered do if IsPedInVehicle(playerPed, vehicle, false) then hasEntered = true vehicleInfo.occupied = true vehicleInfo.driver = driver -- Info-Text verstecken lib.hideTextUI() print("^2[TAXI STATIONS DEBUG]^7 Player entered successfully") -- Prüfen ob Spieler wirklich hinten sitzt local playerSeat = GetPlayerVehicleSeat(playerPed, vehicle) print("^2[TAXI STATIONS DEBUG]^7 Player is in seat: " .. tostring(playerSeat)) if playerSeat == -1 then -- Fahrersitz print("^3[TAXI STATIONS DEBUG]^7 Player is in driver seat, moving to passenger area") if driver and DoesEntityExist(driver) then -- Spieler zum nächsten verfügbaren Sitz bewegen Wait(1000) TaskShuffleToNextVehicleSeat(playerPed, vehicle) Wait(2000) end end lib.notify({ title = 'Taxi Service', description = 'Willkommen im Taxi! Wähle dein Ziel.', type = 'success' }) -- Ziel-Menu öffnen Wait(1000) -- Kurz warten damit Einsteigen abgeschlossen ist OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, vehicleInfo.data.pricePerKm) break end Wait(100) end if not hasEntered then print("^1[TAXI STATIONS DEBUG]^7 Player failed to enter vehicle") -- Info-Text verstecken lib.hideTextUI() lib.notify({ title = 'Taxi Service', description = 'Einsteigen fehlgeschlagen', type = 'error' }) -- Cleanup if driver and DoesEntityExist(driver) then DeleteEntity(driver) end SetVehicleDoorsLocked(vehicle, 2) vehicleInfo.occupied = false end end) end) function OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm) print("^2[TAXI STATIONS DEBUG]^7 Opening station taxi menu") local options = {} -- Bekannte Ziele hinzufügen for _, destination in pairs(Config.KnownDestinations) do local customPrice = math.max(Config.MinFare, math.ceil((CalculateDistanceToCoords(destination.coords) / 1000) * pricePerKm)) table.insert(options, { title = destination.name, description = 'Preis: $' .. customPrice .. ' | Entfernung: ' .. math.ceil(CalculateDistanceToCoords(destination.coords) / 1000 * 100) / 100 .. 'km', icon = 'map-marker', onSelect = function() StartStationTaxiRide(stationId, vehicleId, vehicle, driver, destination.coords, customPrice) end }) end -- Andere Taxi-Stationen als Ziele table.insert(options, { title = '📍 Andere Taxi-Stationen', description = 'Fahre zu einer anderen Taxi-Station', icon = 'taxi', onSelect = function() OpenStationSelectionMenu(stationId, vehicleId, vehicle, driver, pricePerKm) end }) -- Waypoint Option table.insert(options, { title = 'Zu meinem Waypoint', description = 'Fahre zu deinem gesetzten Waypoint', icon = 'location-dot', onSelect = function() local waypoint = GetFirstBlipInfoId(8) if DoesBlipExist(waypoint) then local coords = GetBlipInfoIdCoord(waypoint) local distance = CalculateDistanceToCoords(coords) / 1000 local price = math.max(Config.MinFare, math.ceil(distance * pricePerKm)) StartStationTaxiRide(stationId, vehicleId, vehicle, driver, coords, price) else lib.notify({ title = 'Taxi Service', description = 'Du hast keinen Waypoint gesetzt', type = 'error' }) OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm) end end }) -- Selbst fahren Option (wenn kein Fahrer) if not driver or not DoesEntityExist(driver) then table.insert(options, { title = '🚗 Selbst fahren', description = 'Du fährst das Taxi selbst (kostenlos)', icon = 'car', onSelect = function() SelfDriveStationTaxi(stationId, vehicleId, vehicle) end }) end -- Aussteigen Option table.insert(options, { title = 'Aussteigen', description = 'Das Taxi verlassen', icon = 'door-open', onSelect = function() ExitStationTaxi(stationId, vehicleId, vehicle, driver) end }) lib.registerContext({ id = 'station_taxi_menu', title = 'Taxi - Ziel wählen (' .. Config.TaxiStations[stationId].name .. ')', options = options }) lib.showContext('station_taxi_menu') end function SelfDriveStationTaxi(stationId, vehicleId, vehicle) print("^2[TAXI STATIONS DEBUG]^7 Player driving taxi themselves") local playerPed = PlayerPedId() function SelfDriveStationTaxi(stationId, vehicleId, vehicle) print("^2[TAXI STATIONS DEBUG]^7 Player driving taxi themselves") local playerPed = PlayerPedId() -- Spieler zum Fahrersitz bewegen TaskShuffleToNextVehicleSeat(playerPed, vehicle) lib.notify({ title = 'Taxi Service', description = 'Du fährst das Taxi jetzt selbst. Bringe es zur Station zurück wenn du fertig bist.', type = 'info' }) -- Überwachung für Rückgabe CreateThread(function() while DoesEntityExist(vehicle) do local playerPed = PlayerPedId() -- Prüfen ob Spieler noch im Fahrzeug ist if not IsPedInVehicle(playerPed, vehicle, false) then print("^2[TAXI STATIONS DEBUG]^7 Player left self-drive taxi") -- Nach 30 Sekunden Taxi zurück zur Station SetTimeout(30000, function() ReturnTaxiToStation(stationId, vehicleId, vehicle, nil) end) break end Wait(5000) end end) end function OpenStationSelectionMenu(stationId, vehicleId, vehicle, driver, pricePerKm) print("^2[TAXI STATIONS DEBUG]^7 Opening station selection menu") local options = {} for otherStationId, station in pairs(Config.TaxiStations) do if otherStationId ~= stationId then local distance = CalculateDistanceToCoords(station.blipCoords) / 1000 local price = math.max(Config.MinFare, math.ceil(distance * pricePerKm)) table.insert(options, { title = station.name, description = 'Preis: $' .. price .. ' | Entfernung: ' .. math.ceil(distance * 100) / 100 .. 'km', icon = 'building', onSelect = function() StartStationTaxiRide(stationId, vehicleId, vehicle, driver, station.blipCoords, price) end }) end end -- Zurück Option table.insert(options, { title = '← Zurück', description = 'Zurück zum Hauptmenü', icon = 'arrow-left', onSelect = function() OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm) end }) lib.registerContext({ id = 'station_selection_menu', title = 'Taxi-Stationen', options = options }) lib.showContext('station_selection_menu') end function StartStationTaxiRide(stationId, vehicleId, vehicle, driver, destination, price) print("^2[TAXI STATIONS DEBUG]^7 Starting station taxi ride to: " .. tostring(destination.x) .. ", " .. tostring(destination.y) .. ", " .. tostring(destination.z)) -- Wenn kein Fahrer, Spieler selbst fahren lassen if not driver or not DoesEntityExist(driver) then lib.notify({ title = 'Taxi Service', description = 'Kein Fahrer verfügbar. Fahre selbst zum Ziel! (Kostenlos)', type = 'info' }) -- Destination Blip erstellen local destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z) SetBlipSprite(destinationBlip, 1) SetBlipColour(destinationBlip, 2) SetBlipScale(destinationBlip, 0.8) SetBlipRoute(destinationBlip, true) -- Route zum Ziel anzeigen BeginTextCommandSetBlipName("STRING") AddTextComponentString("Taxi Ziel") EndTextCommandSetBlipName(destinationBlip) -- Route setzen SetNewWaypoint(destination.x, destination.y) return end lib.notify({ title = 'Taxi Service', description = 'Fahrt gestartet - Preis: $' .. price, type = 'success' }) -- Destination Blip erstellen local destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z) SetBlipSprite(destinationBlip, 1) SetBlipColour(destinationBlip, 2) SetBlipScale(destinationBlip, 0.8) SetBlipRoute(destinationBlip, true) -- Route zum Ziel anzeigen BeginTextCommandSetBlipName("STRING") AddTextComponentString("Taxi Ziel") EndTextCommandSetBlipName(destinationBlip) -- Zum Ziel fahren mit verbesserter Navigation TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 25.0, 786603, 5.0) -- Fahrt überwachen CreateThread(function() local lastPos = GetEntityCoords(vehicle) local stuckCounter = 0 local maxStuckCount = 5 local rideTimeout = GetGameTimer() + (5 * 60 * 1000) -- 5 Minuten Timeout while DoesEntityExist(vehicle) and DoesEntityExist(driver) do local vehicleCoords = GetEntityCoords(vehicle) local distance = #(vector3(destination.x, destination.y, destination.z) - vehicleCoords) local distanceMoved = #(lastPos - vehicleCoords) -- Überprüfen ob wir angekommen sind if distance < 10.0 then -- Angekommen TaskVehicleTempAction(driver, vehicle, 27, 3000) print("^2[TAXI STATIONS DEBUG]^7 Arrived at destination") lib.notify({ title = 'Taxi Service', description = 'Du bist angekommen! Preis: $' .. price, type = 'success' }) -- Bezahlung TriggerServerEvent('taxi:payFare', price) -- Blip entfernen RemoveBlip(destinationBlip) -- Nach 10 Sekunden Taxi zurück zur Station SetTimeout(10000, function() ReturnTaxiToStation(stationId, vehicleId, vehicle, driver) end) break end -- Überprüfen ob das Taxi stecken geblieben ist if distanceMoved < 1.0 then stuckCounter = stuckCounter + 1 if stuckCounter >= maxStuckCount then print("^1[TAXI STATIONS DEBUG]^7 Taxi stuck during ride, attempting recovery") -- Versuche, das Taxi zu befreien ClearPedTasks(driver) -- Rückwärts fahren TaskVehicleTempAction(driver, vehicle, 8, 2000) -- Reverse Wait(2000) -- Drehen TaskVehicleTempAction(driver, vehicle, 7, 2000) -- Turn left Wait(1000) TaskVehicleTempAction(driver, vehicle, 8, 1000) -- Reverse Wait(1000) TaskVehicleTempAction(driver, vehicle, 6, 2000) -- Turn right Wait(1000) -- Neue Route zum Ziel TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 25.0, 786603, 5.0) stuckCounter = 0 end else stuckCounter = math.max(0, stuckCounter - 1) end -- Überprüfen ob die Fahrt zu lange dauert if GetGameTimer() > rideTimeout then print("^1[TAXI STATIONS DEBUG]^7 Taxi ride timed out!") lib.notify({ title = 'Taxi Service', description = 'Die Fahrt dauert zu lange. Wir sind fast da!', type = 'warning' }) -- Teleportiere Taxi in die Nähe des Ziels local offset = vector3( math.random(-20, 20), math.random(-20, 20), 0 ) local nearDestination = vector3(destination.x, destination.y, destination.z) + offset -- Finde gültige Z-Koordinate local success, groundZ = GetGroundZFor_3dCoord(nearDestination.x, nearDestination.y, nearDestination.z, true) if success then nearDestination = vector3(nearDestination.x, nearDestination.y, groundZ) end -- Teleportiere Taxi SetEntityCoords(vehicle, nearDestination.x, nearDestination.y, nearDestination.z, false, false, false, false) -- Neues Timeout setzen (1 Minute) rideTimeout = GetGameTimer() + (60 * 1000) end lastPos = vehicleCoords Wait(2000) end end) end function ExitStationTaxi(stationId, vehicleId, vehicle, driver) print("^2[TAXI STATIONS DEBUG]^7 Player exiting station taxi") local playerPed = PlayerPedId() TaskLeaveVehicle(playerPed, vehicle, 0) lib.notify({ title = 'Taxi Service', description = 'Du bist ausgestiegen', type = 'info' }) -- Taxi zurück zur Station nach 5 Sekunden SetTimeout(5000, function() ReturnTaxiToStation(stationId, vehicleId, vehicle, driver) end) end function ReturnTaxiToStation(stationId, vehicleId, vehicle, driver) print("^2[TAXI STATIONS DEBUG]^7 Returning taxi to station: " .. stationId .. "/" .. vehicleId) if not stationVehicles[stationId] or not stationVehicles[stationId][vehicleId] then print("^1[TAXI STATIONS DEBUG]^7 Station vehicle data not found for return") return end if not DoesEntityExist(vehicle) then print("^1[TAXI STATIONS DEBUG]^7 Vehicle doesn't exist anymore") -- Fahrzeug als nicht besetzt markieren stationVehicles[stationId][vehicleId].occupied = false stationVehicles[stationId][vehicleId].driver = nil stationVehicles[stationId][vehicleId].entity = nil -- Nach Respawn-Zeit neues Fahrzeug spawnen print("^2[TAXI STATIONS DEBUG]^7 Scheduling respawn in " .. Config.StationTaxiRespawnTime .. " seconds") SetTimeout(Config.StationTaxiRespawnTime * 1000, function() if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] then local vehicleData = stationVehicles[stationId][vehicleId].data print("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station") SpawnStationVehicle(stationId, vehicleId, vehicleData) end end) return end -- Wenn Fahrer existiert, Taxi zur Station zurückfahren lassen if driver and DoesEntityExist(driver) then print("^2[TAXI STATIONS DEBUG]^7 Making taxi drive back to station") -- Zufällige Position in der Nähe der Station finden local stationCoords = Config.TaxiStations[stationId].coords -- Taxi zur Station zurückfahren lassen TaskVehicleDriveToCoordLongrange(driver, vehicle, stationCoords.x, stationCoords.y, stationCoords.z, 25.0, 786603, 5.0) -- qb-target entfernen während der Fahrt exports['qb-target']:RemoveTargetEntity(vehicle) -- Nach 10 Sekunden tatsächlich löschen SetTimeout(10000, function() -- Fahrer löschen if driver and DoesEntityExist(driver) then DeleteEntity(driver) print("^2[TAXI STATIONS DEBUG]^7 Driver deleted") end -- Fahrzeug löschen if DoesEntityExist(vehicle) then DeleteEntity(vehicle) print("^2[TAXI STATIONS DEBUG]^7 Vehicle deleted") end -- Fahrzeug als nicht besetzt markieren stationVehicles[stationId][vehicleId].occupied = false stationVehicles[stationId][vehicleId].driver = nil stationVehicles[stationId][vehicleId].entity = nil -- Nach Respawn-Zeit neues Fahrzeug spawnen print("^2[TAXI STATIONS DEBUG]^7 Scheduling respawn in " .. Config.StationTaxiRespawnTime .. " seconds") SetTimeout(Config.StationTaxiRespawnTime * 1000, function() if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] then local vehicleData = stationVehicles[stationId][vehicleId].data print("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station") SpawnStationVehicle(stationId, vehicleId, vehicleData) end end) end) else -- Sofort löschen wenn kein Fahrer da ist -- qb-target entfernen if DoesEntityExist(vehicle) then exports['qb-target']:RemoveTargetEntity(vehicle) DeleteEntity(vehicle) print("^2[TAXI STATIONS DEBUG]^7 Vehicle deleted") end -- Fahrzeug als nicht besetzt markieren stationVehicles[stationId][vehicleId].occupied = false stationVehicles[stationId][vehicleId].driver = nil stationVehicles[stationId][vehicleId].entity = nil -- Nach Respawn-Zeit neues Fahrzeug spawnen print("^2[TAXI STATIONS DEBUG]^7 Scheduling respawn in " .. Config.StationTaxiRespawnTime .. " seconds") SetTimeout(Config.StationTaxiRespawnTime * 1000, function() if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] then local vehicleData = stationVehicles[stationId][vehicleId].data print("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station") SpawnStationVehicle(stationId, vehicleId, vehicleData) end end) end end function CalculateDistanceToCoords(coords) local playerCoords = GetEntityCoords(PlayerPedId()) return #(playerCoords - coords) end -- Command um nächste Taxi-Station zu finden RegisterCommand('nearesttaxi', function() local playerCoords = GetEntityCoords(PlayerPedId()) local nearestStation = nil local nearestDistance = math.huge for stationId, station in pairs(Config.TaxiStations) do local distance = #(playerCoords - station.blipCoords) if distance < nearestDistance then nearestDistance = distance nearestStation = {id = stationId, data = station, distance = distance} end end if nearestStation then lib.notify({ title = 'Taxi Service', description = 'Nächste Station: ' .. nearestStation.data.name .. ' (' .. math.ceil(nearestDistance) .. 'm)', type = 'info' }) -- Waypoint zur nächsten Station setzen SetNewWaypoint(nearestStation.data.blipCoords.x, nearestStation.data.blipCoords.y) else lib.notify({ title = 'Taxi Service', description = 'Keine Taxi-Station gefunden', type = 'error' }) end end) -- Event für Admin Respawn RegisterNetEvent('taxi:respawnAllStations', function() print("^2[TAXI STATIONS DEBUG]^7 Respawning all stations...") -- Alle bestehenden Fahrzeuge löschen for stationId, vehicles in pairs(stationVehicles) do for vehicleId, vehicleInfo in pairs(vehicles) do if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity) DeleteEntity(vehicleInfo.entity) end if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then DeleteEntity(vehicleInfo.driver) end end end -- Alle Blips entfernen for _, blip in pairs(stationBlips) do RemoveBlip(blip) end -- Variablen zurücksetzen stationVehicles = {} stationBlips = {} -- Stationen neu initialisieren Wait(1000) InitializeTaxiStations() lib.notify({ title = 'Taxi Service', description = 'Alle Taxi-Stationen wurden neu gespawnt', type = 'success' }) end) -- Thread zum Überwachen der Tasten im Stations-Taxi CreateThread(function() while true do Wait(0) local playerPed = PlayerPedId() local inStationTaxi = false local currentStationTaxi = nil local currentStationId = nil local currentVehicleId = nil local currentDriver = nil local pricePerKm = 0 -- Prüfen ob Spieler in einem Stations-Taxi sitzt for stationId, vehicles in pairs(stationVehicles) do for vehicleId, vehicleInfo in pairs(vehicles) do if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) and vehicleInfo.occupied then if IsPedInVehicle(playerPed, vehicleInfo.entity, false) then inStationTaxi = true currentStationTaxi = vehicleInfo.entity currentStationId = stationId currentVehicleId = vehicleId currentDriver = vehicleInfo.driver pricePerKm = vehicleInfo.data.pricePerKm break end end end if inStationTaxi then break end end if inStationTaxi and currentStationTaxi then -- Zeige Hinweise an local helpText = '[E] - Ziel wählen [F] - Fahrt beenden' lib.showTextUI(helpText, { position = "top-center", icon = 'taxi', style = { borderRadius = 10, backgroundColor = '#48BB78', color = 'white' } }) -- Wenn E gedrückt wird, öffne Menü if IsControlJustReleased(0, 38) then -- E Taste OpenStationTaxiMenu(currentStationId, currentVehicleId, currentStationTaxi, currentDriver, pricePerKm) end -- Wenn F gedrückt wird, beende Fahrt if IsControlJustReleased(0, 23) then -- F Taste lib.hideTextUI() EndStationTaxiRide(currentStationId, currentVehicleId, currentStationTaxi, currentDriver) end else -- Nicht in einem Stations-Taxi lib.hideTextUI() Wait(1000) end end end) -- Funktion zum Beenden der Stations-Taxi Fahrt function EndStationTaxiRide(stationId, vehicleId, vehicle, driver) print("^2[TAXI STATIONS DEBUG]^7 Ending station taxi ride") if not vehicle or not DoesEntityExist(vehicle) then return end local playerPed = PlayerPedId() -- Fahrt beenden Benachrichtigung lib.notify({ title = 'Taxi Service', description = 'Fahrt beendet. Du steigst aus.', type = 'info' }) -- Spieler aussteigen lassen TaskLeaveVehicle(playerPed, vehicle, 0) -- Warten bis ausgestiegen CreateThread(function() local timeout = GetGameTimer() + 5000 while GetGameTimer() < timeout do if not IsPedInVehicle(playerPed, vehicle, false) then -- Spieler ist ausgestiegen break end Wait(100) end -- Taxi nach 5 Sekunden zurück zur Station SetTimeout(5000, function() ReturnTaxiToStation(stationId, vehicleId, vehicle, driver) end) end) end -- Thread zum Überwachen der Tasten im Stations-Taxi CreateThread(function() while true do Wait(0) local playerPed = PlayerPedId() local inStationTaxi = false local currentStationTaxi = nil local currentStationId = nil local currentVehicleId = nil local currentDriver = nil local pricePerKm = 0 -- Prüfen ob Spieler in einem Stations-Taxi sitzt for stationId, vehicles in pairs(stationVehicles) do for vehicleId, vehicleInfo in pairs(vehicles) do if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) and vehicleInfo.occupied then if IsPedInVehicle(playerPed, vehicleInfo.entity, false) then inStationTaxi = true currentStationTaxi = vehicleInfo.entity currentStationId = stationId currentVehicleId = vehicleId currentDriver = vehicleInfo.driver pricePerKm = vehicleInfo.data.pricePerKm break end end end if inStationTaxi then break end end if inStationTaxi and currentStationTaxi then -- Zeige Hinweise an local helpText = '[E] - Ziel wählen [F] - Fahrt beenden' lib.showTextUI(helpText, { position = "top-center", icon = 'taxi', style = { borderRadius = 10, backgroundColor = '#48BB78', color = 'white' } }) -- Wenn E gedrückt wird, öffne Menü if IsControlJustReleased(0, 38) then -- E Taste OpenStationTaxiMenu(currentStationId, currentVehicleId, currentStationTaxi, currentDriver, pricePerKm) end -- Wenn F gedrückt wird, beende Fahrt if IsControlJustReleased(0, 23) then -- F Taste lib.hideTextUI() EndStationTaxiRide(currentStationId, currentVehicleId, currentStationTaxi, currentDriver) end else -- Nicht in einem Stations-Taxi lib.hideTextUI() Wait(1000) end end end) -- Cleanup beim Resource Stop AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then print("^2[TAXI STATIONS DEBUG]^7 Cleaning up stations...") -- TextUI verstecken falls noch angezeigt lib.hideTextUI() -- Alle Station-Fahrzeuge löschen for stationId, vehicles in pairs(stationVehicles) do for vehicleId, vehicleInfo in pairs(vehicles) do if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity) DeleteEntity(vehicleInfo.entity) end if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then DeleteEntity(vehicleInfo.driver) end end end -- Alle Blips entfernen for _, blip in pairs(stationBlips) do RemoveBlip(blip) end print("^2[TAXI STATIONS DEBUG]^7 Cleanup completed") end end) -- Cleanup beim Resource Stop AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then print("^2[TAXI STATIONS DEBUG]^7 Cleaning up stations...") -- TextUI verstecken falls noch angezeigt lib.hideTextUI() -- Alle Station-Fahrzeuge löschen for stationId, vehicles in pairs(stationVehicles) do for vehicleId, vehicleInfo in pairs(vehicles) do if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity) DeleteEntity(vehicleInfo.entity) end if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then DeleteEntity(vehicleInfo.driver) end end end -- Alle Blips entfernen for _, blip in pairs(stationBlips) do RemoveBlip(blip) end print("^2[TAXI STATIONS DEBUG]^7 Cleanup completed") end end) end