1576 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			1576 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| 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
 | |
| 
 | |
| -- Fortgeschrittenes Taxi-Fahrer-Verhaltenssystem
 | |
| function InitializeTaxiDriverAI(driver, vehicle)
 | |
|     if not driver or not DoesEntityExist(driver) then return end
 | |
|     
 | |
|     print("^2[TAXI STATIONS DEBUG]^7 Initializing advanced taxi driver AI")
 | |
|     
 | |
|     -- Fahrer-Persönlichkeit und Fähigkeiten zufällig festlegen
 | |
|     local driverData = {
 | |
|         personality = {
 | |
|             patience = math.random(7, 10) / 10,           -- 0.7-1.0: Wie geduldig ist der Fahrer
 | |
|             caution = math.random(6, 10) / 10,            -- 0.6-1.0: Wie vorsichtig fährt er
 | |
|             speedPreference = math.random(15, 25),        -- 15-25: Bevorzugte Geschwindigkeit
 | |
|             trafficRuleCompliance = math.random(8, 10)/10 -- 0.8-1.0: Wie genau hält er Verkehrsregeln ein
 | |
|         },
 | |
|         state = {
 | |
|             stuckCounter = 0,
 | |
|             lastStuckRecovery = 0,
 | |
|             lastRouteRecalculation = 0,
 | |
|             currentBehavior = "normal", -- normal, cautious, stuck, recovery
 | |
|             lastSpeedCheck = 0,
 | |
|             speedHistory = {},
 | |
|             lastPositions = {},
 | |
|             trafficLightWaitStart = 0,
 | |
|             isWaitingAtTrafficLight = false
 | |
|         },
 | |
|         settings = {
 | |
|             maxStuckCounter = math.random(15, 25),        -- Wie lange warten bis Befreiungsversuch
 | |
|             stuckThreshold = 0.5,                         -- Bewegungsschwelle für Steckenbleiben
 | |
|             checkInterval = 2000,                         -- Überprüfungsintervall in ms
 | |
|             positionHistorySize = 5,                      -- Anzahl der zu speichernden Positionen
 | |
|             minRecoveryInterval = 25000,                  -- Min. Zeit zwischen Befreiungsversuchen
 | |
|             minRouteRecalcInterval = 30000,               -- Min. Zeit zwischen Routenneuberechnungen
 | |
|             trafficLightMaxWait = 45000                   -- Max. Wartezeit an Ampeln
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     -- Fahrer-Verhalten basierend auf Persönlichkeit einstellen
 | |
|     SetDriverAbility(driver, driverData.personality.caution)
 | |
|     SetDriverAggressiveness(driver, 1.0 - driverData.personality.caution)
 | |
|     
 | |
|     -- Fahrer-Daten im Entity speichern
 | |
|     Entity(vehicle).state.driverData = driverData
 | |
|     
 | |
|     -- Fahrer-Verhalten-Thread starten
 | |
|     CreateThread(function()
 | |
|         local lastPos = GetEntityCoords(vehicle)
 | |
|         local lastCheck = GetGameTimer()
 | |
|         
 | |
|         while DoesEntityExist(vehicle) and DoesEntityExist(driver) do
 | |
|             Wait(driverData.settings.checkInterval)
 | |
|             
 | |
|             local currentTime = GetGameTimer()
 | |
|             local timeDelta = currentTime - lastCheck
 | |
|             lastCheck = currentTime
 | |
|             
 | |
|             -- Aktuelle Position und Geschwindigkeit
 | |
|             local currentPos = GetEntityCoords(vehicle)
 | |
|             local speed = GetEntitySpeed(vehicle)
 | |
|             local distanceMoved = #(lastPos - currentPos)
 | |
|             
 | |
|             -- Position für Historie speichern
 | |
|             table.insert(driverData.state.lastPositions, 1, currentPos)
 | |
|             if #driverData.state.lastPositions > driverData.settings.positionHistorySize then
 | |
|                 table.remove(driverData.state.lastPositions)
 | |
|             end
 | |
|             
 | |
|             -- Geschwindigkeit für Historie speichern
 | |
|             table.insert(driverData.state.speedHistory, 1, speed)
 | |
|             if #driverData.state.speedHistory > 5 then
 | |
|                 table.remove(driverData.state.speedHistory)
 | |
|             end
 | |
|             
 | |
|             -- Durchschnittsgeschwindigkeit berechnen
 | |
|             local avgSpeed = 0
 | |
|             for _, s in ipairs(driverData.state.speedHistory) do
 | |
|                 avgSpeed = avgSpeed + s
 | |
|             end
 | |
|             avgSpeed = avgSpeed / #driverData.state.speedHistory
 | |
|             
 | |
|             -- Ampel-Erkennung
 | |
|             local isAtTrafficLight = IsVehicleStoppedAtTrafficLights(vehicle)
 | |
|             
 | |
|             -- Ampel-Wartezustand aktualisieren
 | |
|             if isAtTrafficLight and not driverData.state.isWaitingAtTrafficLight then
 | |
|                 -- Gerade an Ampel angekommen
 | |
|                 driverData.state.isWaitingAtTrafficLight = true
 | |
|                 driverData.state.trafficLightWaitStart = currentTime
 | |
|                 print("^3[TAXI STATIONS DEBUG]^7 Taxi waiting at traffic light")
 | |
|             elseif isAtTrafficLight and driverData.state.isWaitingAtTrafficLight then
 | |
|                 -- Immer noch an Ampel
 | |
|                 local waitTime = currentTime - driverData.state.trafficLightWaitStart
 | |
|                 
 | |
|                 -- Wenn zu lange an Ampel, versuche weiterzufahren (Ampel könnte hängen)
 | |
|                 if waitTime > driverData.settings.trafficLightMaxWait then
 | |
|                     print("^3[TAXI STATIONS DEBUG]^7 Taxi waited too long at traffic light, trying to continue")
 | |
|                     -- Kurz vorwärts fahren um Ampel zu überwinden
 | |
|                     TaskVehicleTempAction(driver, vehicle, 1, 2000) -- Forward
 | |
|                     Wait(2000)
 | |
|                     -- Dann normale Fahrt fortsetzen
 | |
|                     TaxiDriverContinueRoute(driver, vehicle)
 | |
|                     driverData.state.isWaitingAtTrafficLight = false
 | |
|                 end
 | |
|             elseif not isAtTrafficLight and driverData.state.isWaitingAtTrafficLight then
 | |
|                 -- Ampel verlassen
 | |
|                 driverData.state.isWaitingAtTrafficLight = false
 | |
|                 print("^2[TAXI STATIONS DEBUG]^7 Taxi continued after traffic light")
 | |
|             end
 | |
|             
 | |
|             -- Steckenbleiben-Erkennung (nicht an Ampel und kaum Bewegung)
 | |
|             if not isAtTrafficLight and distanceMoved < driverData.settings.stuckThreshold and speed < 0.5 then
 | |
|                 driverData.state.stuckCounter = driverData.state.stuckCounter + 1
 | |
|                 
 | |
|                 -- Nur alle 5 Zähler-Erhöhungen loggen
 | |
|                 if driverData.state.stuckCounter % 5 == 0 then
 | |
|                     print("^3[TAXI STATIONS DEBUG]^7 Taxi might be stuck: " .. driverData.state.stuckCounter .. "/" .. driverData.settings.maxStuckCounter)
 | |
|                 end
 | |
|                 
 | |
|                 -- Wenn lange genug steckengeblieben und genug Zeit seit letztem Versuch
 | |
|                 if driverData.state.stuckCounter >= driverData.settings.maxStuckCounter and 
 | |
|                    (currentTime - driverData.state.lastStuckRecovery) > driverData.settings.minRecoveryInterval then
 | |
|                     
 | |
|                     print("^1[TAXI STATIONS DEBUG]^7 Taxi is stuck, attempting intelligent recovery")
 | |
|                     driverData.state.lastStuckRecovery = currentTime
 | |
|                     driverData.state.currentBehavior = "recovery"
 | |
|                     
 | |
|                     -- Intelligente Befreiung basierend auf Umgebung
 | |
|                     TaxiDriverIntelligentRecovery(driver, vehicle)
 | |
|                     
 | |
|                     -- Steckenbleiben-Zähler teilweise zurücksetzen
 | |
|                     driverData.state.stuckCounter = math.floor(driverData.settings.maxStuckCounter * 0.4)
 | |
|                 end
 | |
|             else
 | |
|                 -- Wenn sich das Fahrzeug bewegt oder an einer Ampel steht
 | |
|                 if isAtTrafficLight then
 | |
|                     -- An Ampel: Zähler langsamer reduzieren
 | |
|                     driverData.state.stuckCounter = math.max(0, driverData.state.stuckCounter - 0.2)
 | |
|                 else
 | |
|                     -- In Bewegung: Zähler reduzieren
 | |
|                     driverData.state.stuckCounter = math.max(0, driverData.state.stuckCounter - 1)
 | |
|                     
 | |
|                     -- Wenn Fahrzeug sich bewegt, aber sehr langsam über längere Zeit
 | |
|                     if avgSpeed < 2.0 and driverData.state.stuckCounter > driverData.settings.maxStuckCounter * 0.5 and
 | |
|                        (currentTime - driverData.state.lastRouteRecalculation) > driverData.settings.minRouteRecalcInterval then
 | |
|                         
 | |
|                         print("^3[TAXI STATIONS DEBUG]^7 Taxi moving too slow, recalculating route")
 | |
|                         driverData.state.lastRouteRecalculation = currentTime
 | |
|                         
 | |
|                         -- Route neu berechnen
 | |
|                         TaxiDriverRecalculateRoute(driver, vehicle)
 | |
|                     end
 | |
|                 end
 | |
|             end
 | |
|             
 | |
|             -- Verhalten basierend auf Umgebung anpassen
 | |
|             TaxiDriverAdaptBehavior(driver, vehicle, driverData)
 | |
|             
 | |
|             lastPos = currentPos
 | |
|         end
 | |
|     end)
 | |
|     
 | |
|     return driverData
 | |
| end
 | |
| 
 | |
| -- Intelligente Befreiung bei Steckenbleiben
 | |
| function TaxiDriverIntelligentRecovery(driver, vehicle)
 | |
|     if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
 | |
|     
 | |
|     -- Aktuelle Position und Umgebung analysieren
 | |
|     local vehicleCoords = GetEntityCoords(vehicle)
 | |
|     local vehicleHeading = GetEntityHeading(vehicle)
 | |
|     local vehicleForwardVector = GetEntityForwardVector(vehicle)
 | |
|     
 | |
|     -- Prüfen ob Hindernisse vorne, hinten, links, rechts
 | |
|     local forwardClear = not IsPositionOccupied(
 | |
|         vehicleCoords.x + vehicleForwardVector.x * 4.0,
 | |
|         vehicleCoords.y + vehicleForwardVector.y * 4.0,
 | |
|         vehicleCoords.z,
 | |
|         1.0, false, true, false, false, false, 0, false
 | |
|     )
 | |
|     
 | |
|     local backwardClear = not IsPositionOccupied(
 | |
|         vehicleCoords.x - vehicleForwardVector.x * 4.0,
 | |
|         vehicleCoords.y - vehicleForwardVector.y * 4.0,
 | |
|         vehicleCoords.z,
 | |
|         1.0, false, true, false, false, false, 0, false
 | |
|     )
 | |
|     
 | |
|     -- Befreiungsstrategie basierend auf Umgebung
 | |
|     ClearPedTasks(driver)
 | |
|     
 | |
|     if backwardClear then
 | |
|         -- Rückwärts fahren wenn hinten frei
 | |
|         print("^3[TAXI STATIONS DEBUG]^7 Recovery: Backing up")
 | |
|         TaskVehicleTempAction(driver, vehicle, 8, 2000) -- Reverse
 | |
|         Wait(2000)
 | |
|         
 | |
|         if forwardClear then
 | |
|             -- Wenn vorne auch frei, einfach weiterfahren
 | |
|             print("^3[TAXI STATIONS DEBUG]^7 Recovery: Path clear, continuing")
 | |
|             TaxiDriverContinueRoute(driver, vehicle)
 | |
|         else
 | |
|             -- Sonst versuchen zu wenden
 | |
|             print("^3[TAXI STATIONS DEBUG]^7 Recovery: Turning around")
 | |
|             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)
 | |
|             TaxiDriverContinueRoute(driver, vehicle)
 | |
|         end
 | |
|     elseif forwardClear then
 | |
|         -- Wenn nur vorne frei, vorwärts fahren
 | |
|         print("^3[TAXI STATIONS DEBUG]^7 Recovery: Moving forward")
 | |
|         TaskVehicleTempAction(driver, vehicle, 1, 2000) -- Forward
 | |
|         Wait(2000)
 | |
|         TaxiDriverContinueRoute(driver, vehicle)
 | |
|     else
 | |
|         -- Wenn komplett eingeklemmt, versuche zu rütteln
 | |
|         print("^3[TAXI STATIONS DEBUG]^7 Recovery: Trying to wiggle free")
 | |
|         TaskVehicleTempAction(driver, vehicle, 7, 1000) -- Turn left
 | |
|         Wait(1000)
 | |
|         TaskVehicleTempAction(driver, vehicle, 8, 800) -- Reverse
 | |
|         Wait(800)
 | |
|         TaskVehicleTempAction(driver, vehicle, 6, 1000) -- Turn right
 | |
|         Wait(1000)
 | |
|         TaskVehicleTempAction(driver, vehicle, 1, 800) -- Forward
 | |
|         Wait(800)
 | |
|         TaxiDriverContinueRoute(driver, vehicle)
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Route neu berechnen
 | |
| function TaxiDriverRecalculateRoute(driver, vehicle)
 | |
|     if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
 | |
|     
 | |
|     -- Aktuelle Zielposition aus dem Fahrzeug-State ermitteln
 | |
|     -- Da wir die nicht direkt auslesen können, nehmen wir an, dass es das aktuelle Ziel ist
 | |
|     local destination = Entity(vehicle).state.currentDestination
 | |
|     
 | |
|     if destination then
 | |
|         -- Versuche einen alternativen Weg zu finden
 | |
|         print("^3[TAXI STATIONS DEBUG]^7 Recalculating route to destination")
 | |
|         
 | |
|         -- Kurz anhalten
 | |
|         TaskVehicleTempAction(driver, vehicle, 27, 1000) -- Stop
 | |
|         Wait(1000)
 | |
|         
 | |
|         -- Neue Route mit leicht geändertem Fahrstil
 | |
|         local drivingStyle = 786603 -- Normal/Vorsichtig
 | |
|         TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
 | |
|     else
 | |
|         -- Wenn kein Ziel bekannt, einfach weiterfahren
 | |
|         TaxiDriverContinueRoute(driver, vehicle)
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Normale Fahrt fortsetzen
 | |
| function TaxiDriverContinueRoute(driver, vehicle)
 | |
|     if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
 | |
|     
 | |
|     -- Ziel aus dem Fahrzeug-State holen
 | |
|     local destination = Entity(vehicle).state.currentDestination
 | |
|     
 | |
|     if destination then
 | |
|         -- Zum Ziel fahren mit angepasster Fahrweise
 | |
|         local drivingStyle = 786603 -- Normal/Vorsichtig
 | |
|         TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
 | |
|     else
 | |
|         -- Wenn kein Ziel bekannt, einfach geradeaus fahren
 | |
|         TaskVehicleDriveWander(driver, vehicle, 15.0, 786603)
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Fahrverhalten an Umgebung anpassen
 | |
| function TaxiDriverAdaptBehavior(driver, vehicle, driverData)
 | |
|     if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
 | |
|     
 | |
|     -- Aktuelle Geschwindigkeit und Position
 | |
|     local speed = GetEntitySpeed(vehicle)
 | |
|     local vehicleCoords = GetEntityCoords(vehicle)
 | |
|     
 | |
|     -- Verkehrsdichte in der Umgebung prüfen
 | |
|     local vehiclesNearby = 0
 | |
|     local vehicles = GetGamePool('CVehicle')
 | |
|     for _, otherVehicle in ipairs(vehicles) do
 | |
|         if otherVehicle ~= vehicle then
 | |
|             local otherCoords = GetEntityCoords(otherVehicle)
 | |
|             local distance = #(vehicleCoords - otherCoords)
 | |
|             if distance < 15.0 then
 | |
|                 vehiclesNearby = vehiclesNearby + 1
 | |
|             end
 | |
|         end
 | |
|     end
 | |
|     
 | |
|     -- Verhalten basierend auf Verkehrsdichte anpassen
 | |
|     local targetSpeed = driverData.personality.speedPreference
 | |
|     
 | |
|     if vehiclesNearby > 5 then
 | |
|         -- Viel Verkehr: langsamer und vorsichtiger
 | |
|         targetSpeed = targetSpeed * 0.7
 | |
|         SetDriverAggressiveness(driver, 0.0)
 | |
|     elseif vehiclesNearby > 2 then
 | |
|         -- Moderater Verkehr: etwas langsamer
 | |
|         targetSpeed = targetSpeed * 0.85
 | |
|         SetDriverAggressiveness(driver, 0.1)
 | |
|     else
 | |
|         -- Wenig Verkehr: normale Geschwindigkeit
 | |
|         SetDriverAggressiveness(driver, 0.2)
 | |
|     end
 | |
|     
 | |
|     -- Geschwindigkeit anpassen wenn nötig
 | |
|     if math.abs(speed - targetSpeed) > 5.0 then
 | |
|         if speed < targetSpeed then
 | |
|             -- Beschleunigen
 | |
|             TaskVehicleTempAction(driver, vehicle, 23, 500) -- Gentle forward
 | |
|         else
 | |
|             -- Abbremsen
 | |
|             TaskVehicleTempAction(driver, vehicle, 24, 500) -- Gentle brake
 | |
|         end
 | |
|         
 | |
|         -- Nach der Anpassung normale Fahrt fortsetzen
 | |
|         Wait(500)
 | |
|         TaxiDriverContinueRoute(driver, vehicle)
 | |
|     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)
 | |
|         
 | |
|         -- Fortgeschrittene KI-ähnliche Fahrer-Logik initialisieren
 | |
|         local driverData = InitializeTaxiDriverAI(driver, vehicle)
 | |
| 
 | |
|         -- Zufälligen Fahrer-Namen generieren
 | |
|         local firstNames = {"Max", "Thomas", "Ali", "Mehmet", "Hans", "Peter", "Klaus", "Michael", "Stefan", "Frank"}
 | |
|         local lastNames = {"Müller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Schulz", "Hoffmann"}
 | |
|         local driverName = firstNames[math.random(#firstNames)] .. " " .. lastNames[math.random(#lastNames)]
 | |
| 
 | |
|         -- Fahrer-Name im Fahrzeug-State speichern
 | |
|         Entity(vehicle).state.driverName = driverName
 | |
| 
 | |
|         lib.notify({
 | |
|             title = 'Taxi Service',
 | |
|             description = 'Dein Fahrer ' .. driverName .. ' ist 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()
 | |
|     
 | |
|     -- 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)
 | |
|     
 | |
|     -- Ziel im Fahrzeug-State speichern für die KI-Logik
 | |
|     Entity(vehicle).state.currentDestination = destination
 | |
| 
 | |
|     -- Zum Ziel fahren mit verbesserter Navigation
 | |
|     local drivingStyle = 786603 -- Normal/Vorsichtig
 | |
|     TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
 | |
| 
 | |
|     -- Fahrer-Dialog anzeigen
 | |
|     local driverName = Entity(vehicle).state.driverName or "Taxi-Fahrer"
 | |
|     local dialogOptions = {
 | |
|         "Ich bringe dich sicher ans Ziel.",
 | |
|         "Schönes Wetter heute, oder?",
 | |
|         "Ich kenne eine Abkürzung.",
 | |
|         "Bist du aus der Gegend?",
 | |
|         "Ich fahre schon seit 15 Jahren Taxi.",
 | |
|         "Entspann dich und genieße die Fahrt."
 | |
|     }
 | |
| 
 | |
|     -- Zufälligen Dialog auswählen und anzeigen
 | |
|     Wait(3000) -- Kurz warten bevor der Fahrer spricht
 | |
|     lib.notify({
 | |
|         title = driverName,
 | |
|         description = dialogOptions[math.random(#dialogOptions)],
 | |
|         type = 'info',
 | |
|         icon = 'comment',
 | |
|         position = 'top-center',
 | |
|         duration = 5000
 | |
|     })
 | |
|     
 | |
|     -- Fahrt überwachen (vereinfacht, da KI-Logik die meiste Arbeit übernimmt)
 | |
|     CreateThread(function()
 | |
|         local rideTimeout = GetGameTimer() + (8 * 60 * 1000) -- 8 Minuten Timeout
 | |
|         
 | |
|         while DoesEntityExist(vehicle) and DoesEntityExist(driver) do
 | |
|             local vehicleCoords = GetEntityCoords(vehicle)
 | |
|             local distance = #(vector3(destination.x, destination.y, destination.z) - 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)
 | |
|                 
 | |
|                 -- Fahrer-Dialog anzeigen
 | |
|                 local driverName = Entity(vehicle).state.driverName or "Taxi-Fahrer"
 | |
|                 local arrivalDialogs = {
 | |
|                     "Wir sind da! Das macht dann $" .. price .. ".",
 | |
|                     "Angekommen! $" .. price .. " bitte.",
 | |
|                     "Hier sind wir. $" .. price .. ", bargeldlos ist auch möglich.",
 | |
|                     "Ziel erreicht! Das macht $" .. price .. "."
 | |
|                 }
 | |
|                 
 | |
|                 lib.notify({
 | |
|                     title = driverName,
 | |
|                     description = arrivalDialogs[math.random(#arrivalDialogs)],
 | |
|                     type = 'info',
 | |
|                     icon = 'comment',
 | |
|                     position = 'top-center',
 | |
|                     duration = 5000
 | |
|                 })
 | |
|                 
 | |
|                 -- Nach 10 Sekunden Taxi zurück zur Station
 | |
|                 SetTimeout(10000, function()
 | |
|                     ReturnTaxiToStation(stationId, vehicleId, vehicle, driver)
 | |
|                 end)
 | |
|                 
 | |
|                 break
 | |
|             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
 | |
|             
 | |
|             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 mit geduldiger Fahrweise
 | |
|         local drivingStyle = 786603 -- Normal/Vorsichtig
 | |
|         TaskVehicleDriveToCoordLongrange(driver, vehicle, stationCoords.x, stationCoords.y, stationCoords.z, 20.0, drivingStyle, 10.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
 | |
| 
 | |
| -- 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)
 | |
| 
 | |
|         
 | |
|     
 | |
| 
 | 
