local QBCore = exports['qb-core']:GetCoreObject() local currentTaxi = nil local currentDriver = nil local taxiBlip = nil local mapBlip = nil local destinationBlip = nil local taxiMeter = { isRunning = false, startCoords = nil, currentFare = 0, pricePerKm = 0 } print("^2[TAXI DEBUG]^7 Main script loaded") -- Taxi rufen Command RegisterCommand('taxi', function() print("^2[TAXI DEBUG]^7 Taxi command executed") if currentTaxi and DoesEntityExist(currentTaxi) then print("^1[TAXI DEBUG]^7 Taxi already exists") lib.notify({ title = 'Taxi Service', description = 'Du hast bereits ein Taxi gerufen', type = 'error' }) return end CallTaxi() end) function CallTaxi() print("^2[TAXI DEBUG]^7 CallTaxi function started") lib.notify({ title = 'Taxi Service', description = 'Taxi wird gerufen...', type = 'info' }) CreateThread(function() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) print("^2[TAXI DEBUG]^7 Player coords: " .. tostring(playerCoords)) -- Verbesserte Spawn-Position für Taxi finden local spawnCoords = GetImprovedTaxiSpawnPosition(playerCoords) if not spawnCoords then print("^1[TAXI DEBUG]^7 No spawn position found") lib.notify({ title = 'Taxi Service', description = 'Kein geeigneter Spawn-Punkt gefunden', type = 'error' }) return end print("^2[TAXI DEBUG]^7 Spawn coords found: " .. tostring(spawnCoords.x) .. ", " .. tostring(spawnCoords.y) .. ", " .. tostring(spawnCoords.z)) -- Taxi spawnen local taxi = SpawnTaxi(spawnCoords) if not taxi then print("^1[TAXI DEBUG]^7 Failed to spawn taxi") lib.notify({ title = 'Taxi Service', description = 'Taxi konnte nicht gespawnt werden', type = 'error' }) return end print("^2[TAXI DEBUG]^7 Taxi spawned: " .. taxi) currentTaxi = taxi -- Fahrer spawnen local driver = SpawnTaxiDriver(taxi) if driver then currentDriver = driver print("^2[TAXI DEBUG]^7 Driver spawned: " .. driver) -- Verbesserte Navigation zum Spieler NavigateToPlayer(driver, taxi, playerCoords) -- Blip für Taxi erstellen (Entity-Blip) CreateTaxiBlips(taxi) -- Überwachung des Taxi-Fortschritts MonitorTaxiProgress(taxi, driver, playerCoords) -- Überwachung der Ankunft MonitorTaxiArrival(taxi, driver, playerCoords) lib.notify({ title = 'Taxi Service', description = 'Taxi ist unterwegs zu dir! Verfolge es auf der Karte.', type = 'success' }) else print("^1[TAXI DEBUG]^7 Failed to spawn driver") lib.notify({ title = 'Taxi Service', description = 'Taxi ohne Fahrer gespawnt - Du kannst es selbst fahren', type = 'warning' }) end end) end function GetImprovedTaxiSpawnPosition(playerCoords) print("^2[TAXI DEBUG]^7 Finding optimal spawn position...") -- PRIORITÄT 1: Config-Positionen prüfen if Config.MobileTaxiSpawns and #Config.MobileTaxiSpawns > 0 then print("^2[TAXI DEBUG]^7 Checking config spawn positions first") -- Alle Spawn-Positionen nach Entfernung sortieren local sortedSpawns = {} for i, spawnPos in ipairs(Config.MobileTaxiSpawns) do local distance = #(playerCoords - vector3(spawnPos.x, spawnPos.y, spawnPos.z)) table.insert(sortedSpawns, { coords = spawnPos, distance = distance }) end -- Nach Entfernung sortieren (nächste zuerst) table.sort(sortedSpawns, function(a, b) return a.distance < b.distance end) -- Prüfen ob die Positionen frei sind for i, spawn in ipairs(sortedSpawns) do local spawnCoords = spawn.coords -- Prüfen ob Position frei ist local clearArea = true local vehicles = GetGamePool('CVehicle') -- Prüfen ob andere Fahrzeuge in der Nähe sind for _, vehicle in ipairs(vehicles) do local vehCoords = GetEntityCoords(vehicle) if #(vector3(spawnCoords.x, spawnCoords.y, spawnCoords.z) - vehCoords) < 5.0 then clearArea = false break end end -- Wenn Position frei ist, verwenden if clearArea then print("^2[TAXI DEBUG]^7 Using spawn position from config: " .. tostring(spawnCoords.x) .. ", " .. tostring(spawnCoords.y) .. ", " .. tostring(spawnCoords.z) .. " (Distance: " .. spawn.distance .. "m)") return spawnCoords end end -- Wenn keine Position frei ist, verwende die erste (nächste) Position trotzdem print("^3[TAXI DEBUG]^7 All config positions occupied, using first one anyway: " .. tostring(sortedSpawns[1].coords.x) .. ", " .. tostring(sortedSpawns[1].coords.y) .. ", " .. tostring(sortedSpawns[1].coords.z)) return sortedSpawns[1].coords end -- PRIORITÄT 2: Straßenknotenpunkte suchen print("^2[TAXI DEBUG]^7 No config positions available, trying road nodes") -- Versuche mehrere Straßenknotenpunkte zu finden for i = 1, 10 do local foundNode, nodePos = GetNthClosestVehicleNode(playerCoords.x, playerCoords.y, playerCoords.z, i, 1, 3.0, 0) if foundNode then local nodeDistance = #(playerCoords - nodePos) -- Prüfe ob Position frei ist local clearArea = true local vehicles = GetGamePool('CVehicle') for _, vehicle in ipairs(vehicles) do local vehCoords = GetEntityCoords(vehicle) if #(nodePos - vehCoords) < 5.0 then clearArea = false break end end if clearArea then print("^2[TAXI DEBUG]^7 Using road node at distance: " .. nodeDistance .. "m") return {x = nodePos.x, y = nodePos.y, z = nodePos.z, w = 0.0} end end end -- PRIORITÄT 3: Zufällige Position auf einer Straße generieren print("^3[TAXI DEBUG]^7 Generating random position on road...") -- Versuche bis zu 10 Mal, eine gültige Position auf einer Straße zu finden for attempt = 1, 10 do -- Zufällige Position im Umkreis local angle = math.random() * 2 * math.pi local distance = math.random(50, 150) -- Moderate Entfernung local x = playerCoords.x + math.cos(angle) * distance local y = playerCoords.y + math.sin(angle) * distance local z = playerCoords.z -- Versuche eine gültige Z-Koordinate zu bekommen local success, groundZ = GetGroundZFor_3dCoord(x, y, z, true) if success then -- Prüfe ob die Position auf einer Straße ist local isOnRoad = IsPointOnRoad(x, y, groundZ) if isOnRoad then print("^2[TAXI DEBUG]^7 Found random position on road at distance: " .. distance .. "m") return {x = x, y = y, z = groundZ, w = 0.0} end end end -- NOTFALL-FALLBACK: Zufällige Position in der Nähe print("^1[TAXI DEBUG]^7 Using emergency random spawn position") local angle = math.random() * 2 * math.pi local distance = math.random(30, 100) local x = playerCoords.x + math.cos(angle) * distance local y = playerCoords.y + math.sin(angle) * distance local z = playerCoords.z -- Versuche eine gültige Z-Koordinate zu bekommen local success, groundZ = GetGroundZFor_3dCoord(x, y, z, true) if success then z = groundZ end return {x = x, y = y, z = z, w = 0.0} end function SpawnTaxi(coords) print("^2[TAXI DEBUG]^7 Spawning taxi at: " .. tostring(coords.x) .. ", " .. tostring(coords.y) .. ", " .. tostring(coords.z)) -- Sicherstellen dass wir ein gültiges Taxi-Model haben local taxiModel = nil if Config.TaxiVehicles and #Config.TaxiVehicles > 0 then -- Zufälliges Taxi-Model aus Config wählen local randomIndex = math.random(1, #Config.TaxiVehicles) taxiModel = GetHashKey(Config.TaxiVehicles[randomIndex].model) else taxiModel = GetHashKey("taxi") -- Fallback end print("^2[TAXI DEBUG]^7 Taxi model hash: " .. taxiModel) -- Model laden mit Timeout RequestModel(taxiModel) local modelLoaded = false local timeout = GetGameTimer() + 10000 while not modelLoaded and GetGameTimer() < timeout do modelLoaded = HasModelLoaded(taxiModel) if not modelLoaded then print("^3[TAXI DEBUG]^7 Waiting for taxi model to load...") Wait(100) end end if not modelLoaded then print("^1[TAXI DEBUG]^7 Failed to load taxi model! Trying default model.") SetModelAsNoLongerNeeded(taxiModel) -- Versuche Standard-Taxi als Fallback taxiModel = GetHashKey("taxi") RequestModel(taxiModel) timeout = GetGameTimer() + 5000 while not HasModelLoaded(taxiModel) and GetGameTimer() < timeout do Wait(100) end if not HasModelLoaded(taxiModel) then print("^1[TAXI DEBUG]^7 Failed to load default taxi model!") return nil end end -- Fahrzeug erstellen local taxi = CreateVehicle(taxiModel, coords.x, coords.y, coords.z, coords.w or 0.0, true, false) if not DoesEntityExist(taxi) then print("^1[TAXI DEBUG]^7 Failed to create taxi vehicle!") return nil end print("^2[TAXI DEBUG]^7 Taxi created successfully: " .. taxi) -- Fahrzeug konfigurieren SetEntityAsMissionEntity(taxi, true, true) SetEntityInvincible(taxi, true) SetVehicleCanBeVisiblyDamaged(taxi, false) SetEntityProofs(taxi, true, true, true, true, true, true, true, true) SetVehicleExplodesOnHighExplosionDamage(taxi, false) SetVehicleHasBeenOwnedByPlayer(taxi, true) SetVehicleIsConsideredByPlayer(taxi, true) SetVehicleOnGroundProperly(taxi) SetVehicleEngineOn(taxi, true, true, false) SetVehicleDoorsLocked(taxi, 2) -- Locked initially -- Taxi-Livery setzen falls verfügbar local liveryCount = GetVehicleLiveryCount(taxi) if liveryCount > 0 then SetVehicleLivery(taxi, 0) -- Erste Livery verwenden print("^2[TAXI DEBUG]^7 Taxi livery set") end -- Fahrzeug-Extras aktivieren falls vorhanden for i = 1, 14 do if DoesExtraExist(taxi, i) then SetVehicleExtra(taxi, i, false) -- Extra aktivieren end end -- Fahrzeug-Farbe setzen (Gelb für Taxis) SetVehicleColours(taxi, 88, 88) -- Taxi Yellow SetModelAsNoLongerNeeded(taxiModel) return taxi end function SpawnTaxiDriver(vehicle) print("^2[TAXI DEBUG]^7 Spawning taxi driver...") -- Bessere Fahrer-Models mit Fallbacks local driverModels = { "A_C_Chimp", -- Affe (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 -- Versuche verschiedene Models for i, modelName in ipairs(driverModels) do print("^2[TAXI DEBUG]^7 Trying driver model " .. i .. ": " .. modelName) driverHash = GetHashKey(modelName) -- Model laden 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 -- Alle 1 Sekunde loggen print("^3[TAXI DEBUG]^7 Still waiting for model " .. modelName .. " (attempt " .. attempts .. ")") end Wait(100) end if HasModelLoaded(driverHash) then print("^2[TAXI DEBUG]^7 Model " .. modelName .. " loaded successfully after " .. attempts .. " attempts") -- Fahrer erstellen driver = CreatePedInsideVehicle(vehicle, 26, driverHash, -1, true, false) if DoesEntityExist(driver) then print("^2[TAXI DEBUG]^7 Driver created successfully with model: " .. modelName .. " (ID: " .. driver .. ")") break else print("^1[TAXI DEBUG]^7 Failed to create driver with loaded model: " .. modelName) SetModelAsNoLongerNeeded(driverHash) end else print("^1[TAXI DEBUG]^7 Failed to load driver model: " .. modelName) SetModelAsNoLongerNeeded(driverHash) end end -- Wenn immer noch kein Fahrer erstellt wurde if not driver or not DoesEntityExist(driver) then print("^1[TAXI DEBUG]^7 Could not create any driver! Continuing without driver...") return nil end -- Fahrer konfigurieren print("^2[TAXI DEBUG]^7 Configuring driver...") SetEntityAsMissionEntity(driver, true, true) SetBlockingOfNonTemporaryEvents(driver, true) SetDriverAbility(driver, 1.0) -- Maximale Fahrfähigkeit SetDriverAggressiveness(driver, 0.0) -- Minimale Aggressivität SetPedFleeAttributes(driver, 0, 0) SetPedCombatAttributes(driver, 17, 1) SetPedSeeingRange(driver, 0.0) SetPedHearingRange(driver, 0.0) SetPedAlertness(driver, 0) SetPedKeepTask(driver, true) -- Fahrer-Outfit (nur wenn es ein anpassbarer Ped ist) if driverHash == GetHashKey("mp_m_freemode_01") then print("^2[TAXI DEBUG]^7 Setting driver outfit...") -- Basis-Outfit für Taxi-Fahrer SetPedComponentVariation(driver, 0, 0, 0, 0) -- Face SetPedComponentVariation(driver, 2, 1, 0, 0) -- Hair SetPedComponentVariation(driver, 8, 15, 0, 0) -- Undershirt SetPedComponentVariation(driver, 11, 91, 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 -- Model nicht mehr benötigt if driverHash then SetModelAsNoLongerNeeded(driverHash) end print("^2[TAXI DEBUG]^7 Driver spawn completed successfully") return driver end function CreateTaxiBlips(taxi) -- Blip für Taxi erstellen (Entity-Blip) taxiBlip = AddBlipForEntity(taxi) SetBlipSprite(taxiBlip, 198) SetBlipColour(taxiBlip, 5) SetBlipScale(taxiBlip, 0.8) SetBlipDisplay(taxiBlip, 2) -- Zeigt auf Minimap und großer Karte SetBlipShowCone(taxiBlip, true) -- Zeigt Sichtkegel SetBlipAsShortRange(taxiBlip, false) -- Immer sichtbar, egal wie weit entfernt BeginTextCommandSetBlipName("STRING") AddTextComponentString("Dein Taxi") EndTextCommandSetBlipName(taxiBlip) -- Zusätzlicher Blip auf der Karte für bessere Sichtbarkeit local taxiCoords = GetEntityCoords(taxi) mapBlip = AddBlipForCoord(taxiCoords.x, taxiCoords.y, taxiCoords.z) SetBlipSprite(mapBlip, 198) SetBlipColour(mapBlip, 5) SetBlipScale(mapBlip, 1.0) SetBlipDisplay(mapBlip, 4) -- Nur auf großer Karte BeginTextCommandSetBlipName("STRING") AddTextComponentString("Dein Taxi") EndTextCommandSetBlipName(mapBlip) -- Blip aktualisieren während Taxi unterwegs ist CreateThread(function() while DoesEntityExist(taxi) and mapBlip do local taxiCoords = GetEntityCoords(taxi) SetBlipCoords(mapBlip, taxiCoords.x, taxiCoords.y, taxiCoords.z) Wait(1000) end -- Blip entfernen wenn Thread beendet if mapBlip then RemoveBlip(mapBlip) mapBlip = nil end end) end function NavigateToPlayer(driver, taxi, playerCoords) print("^2[TAXI DEBUG]^7 Navigating taxi to player...") -- Versuche einen guten Wegpunkt in der Nähe des Spielers zu finden local success, nodePos = GetClosestVehicleNodeWithHeading(playerCoords.x, playerCoords.y, playerCoords.z, 1, 3.0, 0) if success then print("^2[TAXI DEBUG]^7 Found good vehicle node near player") -- Zum Wegpunkt fahren TaskVehicleDriveToCoordLongrange(driver, taxi, nodePos.x, nodePos.y, nodePos.z, 25.0, 786603, 5.0) else print("^3[TAXI DEBUG]^7 No good vehicle node found, driving directly to player") -- Direkt zum Spieler fahren TaskVehicleDriveToCoordLongrange(driver, taxi, playerCoords.x, playerCoords.y, playerCoords.z, 25.0, 786603, 5.0) end -- Fahrer-Verhalten verbessern SetDriverAggressiveness(driver, 0.0) SetDriverAbility(driver, 1.0) end function MonitorTaxiProgress(taxi, driver, playerCoords) print("^2[TAXI DEBUG]^7 Monitoring taxi progress...") local lastPos = GetEntityCoords(taxi) local stuckCounter = 0 local maxStuckCount = 5 local totalStuckEvents = 0 local maxTotalStuckEvents = 3 CreateThread(function() while DoesEntityExist(taxi) and DoesEntityExist(driver) do Wait(3000) if not DoesEntityExist(taxi) then print("^1[TAXI DEBUG]^7 Taxi no longer exists! Taxi ID: " .. tostring(taxi)) if currentTaxi == taxi then print("^1[TAXI DEBUG]^7 This was the current active taxi") else print("^1[TAXI DEBUG]^7 This was NOT the current active taxi. Current taxi: " .. tostring(currentTaxi)) end return end if not DoesEntityExist(driver) then print("^1[TAXI DEBUG]^7 Driver no longer exists! Driver ID: " .. tostring(driver)) if currentDriver == driver then print("^1[TAXI DEBUG]^7 This was the current active driver") else print("^1[TAXI DEBUG]^7 This was NOT the current active driver. Current driver: " .. tostring(currentDriver)) end return end local currentPos = GetEntityCoords(taxi) local distanceMoved = #(lastPos - currentPos) local currentPlayerCoords = GetEntityCoords(PlayerPedId()) local distanceToPlayer = #(currentPos - currentPlayerCoords) -- If taxi is close to player, we're done monitoring progress if distanceToPlayer < 15.0 then print("^2[TAXI DEBUG]^7 Taxi arrived near player, stopping progress monitoring") return end -- Check if taxi is stuck (not moving much) if distanceMoved < 1.0 then stuckCounter = stuckCounter + 1 print("^3[TAXI DEBUG]^7 Taxi might be stuck: " .. stuckCounter .. "/" .. maxStuckCount) -- If stuck for too long, try recovery if stuckCounter >= maxStuckCount then totalStuckEvents = totalStuckEvents + 1 print("^1[TAXI DEBUG]^7 Taxi is stuck, attempting recovery #" .. totalStuckEvents) if totalStuckEvents >= maxTotalStuckEvents then print("^1[TAXI DEBUG]^7 Too many stuck events, respawning taxi") lib.notify({ title = 'Taxi Service', description = 'Dein Taxi steckt fest. Ein neues wird gerufen!', type = 'warning' }) -- Despawn current taxi and call a new one DespawnTaxi() Wait(1000) CallTaxi() return else -- Try to recover RecoverStuckTaxi(taxi, driver, currentPlayerCoords) stuckCounter = 0 end end else stuckCounter = math.max(0, stuckCounter - 1) -- Gradually reduce counter if moving end lastPos = currentPos end end) end function RecoverStuckTaxi(taxi, driver, playerCoords) print("^2[TAXI DEBUG]^7 Attempting to recover stuck taxi") -- Clear current task ClearPedTasks(driver) -- Try to back up a bit first TaskVehicleTempAction(driver, taxi, 8, 2000) -- Reverse Wait(2000) -- Try to turn around TaskVehicleTempAction(driver, taxi, 7, 2000) -- Turn left Wait(1000) TaskVehicleTempAction(driver, taxi, 8, 1000) -- Reverse Wait(1000) TaskVehicleTempAction(driver, taxi, 6, 2000) -- Turn right Wait(1000) -- Try to find a new path local found, nodePos = GetClosestVehicleNode(playerCoords.x, playerCoords.y, playerCoords.z, 1, 3.0, 0) if found then print("^2[TAXI DEBUG]^7 Found new path for recovery") TaskVehicleDriveToCoordLongrange(driver, taxi, nodePos.x, nodePos.y, nodePos.z, 25.0, 786603, 5.0) else -- Direct approach as fallback print("^3[TAXI DEBUG]^7 Using direct approach for recovery") TaskVehicleDriveToCoordLongrange(driver, taxi, playerCoords.x, playerCoords.y, playerCoords.z, 25.0, 786603, 5.0) end end function MonitorTaxiArrival(taxi, driver, playerCoords) print("^2[TAXI DEBUG]^7 Monitoring taxi arrival...") local arrivalTimeout = GetGameTimer() + (120 * 1000) -- 2 minute timeout CreateThread(function() while DoesEntityExist(taxi) and (not driver or DoesEntityExist(driver)) do local taxiCoords = GetEntityCoords(taxi) local currentPlayerCoords = GetEntityCoords(PlayerPedId()) local distance = #(currentPlayerCoords - taxiCoords) -- Check if taxi is close to player if distance < 15.0 then print("^2[TAXI DEBUG]^7 Taxi arrived!") -- Taxi stoppen if driver and DoesEntityExist(driver) then TaskVehicleTempAction(driver, taxi, 27, 3000) -- Brake Wait(2000) SetVehicleDoorsLocked(taxi, 1) -- Unlock doors end lib.notify({ title = 'Taxi Service', description = 'Dein Taxi ist angekommen! Steige ein.', type = 'success' }) -- qb-target für Einsteigen hinzufügen exports['qb-target']:AddTargetEntity(taxi, { options = { { type = "client", event = "taxi:enterTaxi", icon = "fas fa-car-side", label = "Ins Taxi einsteigen" } }, distance = 3.0 }) break end -- Check for timeout if GetGameTimer() > arrivalTimeout then print("^1[TAXI DEBUG]^7 Taxi arrival timed out!") lib.notify({ title = 'Taxi Service', description = 'Dein Taxi steckt fest. Ein neues wird gerufen!', type = 'warning' }) -- Despawn current taxi and call a new one DespawnTaxi() Wait(1000) CallTaxi() break end Wait(2000) end end) end -- Event für Einsteigen ins Taxi RegisterNetEvent('taxi:enterTaxi', function() print("^2[TAXI DEBUG]^7 Player entering taxi") if not currentTaxi or not DoesEntityExist(currentTaxi) then print("^1[TAXI DEBUG]^7 No taxi exists") return end local playerPed = PlayerPedId() -- Spieler hinten einsteigen lassen local seatIndex = 1 -- Hinten links if not IsVehicleSeatFree(currentTaxi, 1) then seatIndex = 2 -- Hinten rechts end if not IsVehicleSeatFree(currentTaxi, seatIndex) then seatIndex = 0 -- Beifahrer als Fallback end TaskEnterVehicle(playerPed, currentTaxi, 10000, seatIndex, 1.0, 1, 0) -- Warten bis eingestiegen CreateThread(function() local timeout = GetGameTimer() + 10000 local entered = false while GetGameTimer() < timeout and not entered do if IsPedInVehicle(playerPed, currentTaxi, false) then entered = true print("^2[TAXI DEBUG]^7 Player entered taxi successfully") -- qb-target entfernen exports['qb-target']:RemoveTargetEntity(currentTaxi) lib.notify({ title = 'Taxi Service', description = 'Willkommen im Taxi! Wähle dein Ziel.', type = 'success' }) -- Ziel-Menu öffnen Wait(1000) OpenDestinationMenu() end Wait(100) end if not entered then print("^1[TAXI DEBUG]^7 Player failed to enter taxi") lib.notify({ title = 'Taxi Service', description = 'Einsteigen fehlgeschlagen', type = 'error' }) end end) end) function OpenDestinationMenu() print("^2[TAXI DEBUG]^7 Opening destination menu") local options = {} -- Bekannte Ziele hinzufügen for _, destination in pairs(Config.KnownDestinations) do local distance = CalculateDistanceToCoords(destination.coords) / 1000 -- in km local price = math.max(Config.MinFare, math.ceil(distance * Config.PricePerKm)) table.insert(options, { title = destination.name, description = 'Preis: $' .. price .. ' | Entfernung: ' .. math.ceil(distance * 100) / 100 .. 'km', icon = 'map-marker', onSelect = function() StartTaxiRide(destination.coords, price) end }) 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 * Config.PricePerKm)) StartTaxiRide(coords, price) else lib.notify({ title = 'Taxi Service', description = 'Du hast keinen Waypoint gesetzt', type = 'error' }) OpenDestinationMenu() end end }) -- Selbst fahren Option (wenn kein Fahrer) if not currentDriver or not DoesEntityExist(currentDriver) then table.insert(options, { title = '🚗 Selbst fahren', description = 'Du fährst das Taxi selbst', icon = 'car', onSelect = function() SelfDriveTaxi() end }) end -- Aussteigen Option table.insert(options, { title = 'Aussteigen', description = 'Das Taxi verlassen', icon = 'door-open', onSelect = function() ExitTaxi() end }) lib.registerContext({ id = 'taxi_destination_menu', title = 'Taxi - Ziel wählen', options = options }) lib.showContext('taxi_destination_menu') end function StartTaxiRide(destination, price) print("^2[TAXI DEBUG]^7 Starting taxi ride to: " .. tostring(destination.x) .. ", " .. tostring(destination.y) .. ", " .. tostring(destination.z)) if not currentTaxi or not DoesEntityExist(currentTaxi) then print("^1[TAXI DEBUG]^7 No taxi exists for ride") return end -- Wenn kein Fahrer, Spieler selbst fahren lassen if not currentDriver or not DoesEntityExist(currentDriver) then lib.notify({ title = 'Taxi Service', description = 'Kein Fahrer verfügbar. Du musst selbst fahren!', type = 'warning' }) return end lib.notify({ title = 'Taxi Service', description = 'Fahrt gestartet - Preis: $' .. price, type = 'success' }) -- Destination Blip erstellen if destinationBlip then RemoveBlip(destinationBlip) end destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z) SetBlipSprite(destinationBlip, 1) SetBlipColour(destinationBlip, 2) SetBlipScale(destinationBlip, 0.8) SetBlipRoute(destinationBlip, true) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Taxi Ziel") EndTextCommandSetBlipName(destinationBlip) -- Taximeter starten taxiMeter.isRunning = true taxiMeter.startCoords = GetEntityCoords(currentTaxi) taxiMeter.currentFare = 0 taxiMeter.pricePerKm = Config.PricePerKm -- Zum Ziel fahren mit verbesserter Navigation NavigateToDestination(currentDriver, currentTaxi, destination) -- Fahrt überwachen MonitorTaxiRide(destination, price) end function NavigateToDestination(driver, taxi, destination) print("^2[TAXI DEBUG]^7 Navigating to destination...") -- Versuche einen guten Wegpunkt in der Nähe des Ziels zu finden local success, nodePos = GetClosestVehicleNodeWithHeading(destination.x, destination.y, destination.z, 1, 3.0, 0) if success then print("^2[TAXI DEBUG]^7 Found good vehicle node near destination") -- Zum Wegpunkt fahren mit verbesserter Fahrweise TaskVehicleDriveToCoordLongrange(driver, taxi, nodePos.x, nodePos.y, nodePos.z, 25.0, 786603, 5.0) else print("^3[TAXI DEBUG]^7 No good vehicle node found, driving directly to destination") -- Direkt zum Ziel fahren TaskVehicleDriveToCoordLongrange(driver, taxi, destination.x, destination.y, destination.z, 25.0, 786603, 5.0) end -- Fahrer-Verhalten verbessern SetDriverAggressiveness(driver, 0.0) SetDriverAbility(driver, 1.0) end function MonitorTaxiRide(destination, price) print("^2[TAXI DEBUG]^7 Monitoring taxi ride...") local lastPos = GetEntityCoords(currentTaxi) local stuckCounter = 0 local maxStuckCount = 5 local rideTimeout = GetGameTimer() + (5 * 60 * 1000) -- 5 Minuten Timeout CreateThread(function() while DoesEntityExist(currentTaxi) and DoesEntityExist(currentDriver) do local taxiCoords = GetEntityCoords(currentTaxi) local distance = #(vector3(destination.x, destination.y, destination.z) - taxiCoords) local distanceMoved = #(lastPos - taxiCoords) -- Überprüfen ob wir angekommen sind if distance < 10.0 then -- Angekommen TaskVehicleTempAction(currentDriver, currentTaxi, 27, 3000) print("^2[TAXI DEBUG]^7 Arrived at destination") lib.notify({ title = 'Taxi Service', description = 'Du bist angekommen! Preis: $' .. price, type = 'success' }) -- Bezahlung TriggerServerEvent('taxi:payFare', price) -- Blips entfernen if destinationBlip then RemoveBlip(destinationBlip) destinationBlip = nil end -- Nach 10 Sekunden Taxi despawnen SetTimeout(10000, function() DespawnTaxi() 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 DEBUG]^7 Taxi stuck during ride, attempting recovery") RecoverStuckTaxi(currentTaxi, currentDriver, vector3(destination.x, destination.y, destination.z)) 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 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(currentTaxi, nearDestination.x, nearDestination.y, nearDestination.z, false, false, false, false) -- Neues Timeout setzen (1 Minute) rideTimeout = GetGameTimer() + (60 * 1000) end lastPos = taxiCoords Wait(2000) end end) end function SelfDriveTaxi() print("^2[TAXI DEBUG]^7 Player driving taxi themselves") if not currentTaxi or not DoesEntityExist(currentTaxi) then return end local playerPed = PlayerPedId() -- Fahrer entfernen falls vorhanden if currentDriver and DoesEntityExist(currentDriver) then DeleteEntity(currentDriver) currentDriver = nil end -- Spieler zum Fahrersitz bewegen TaskShuffleToNextVehicleSeat(playerPed, currentTaxi) lib.notify({ title = 'Taxi Service', description = 'Du fährst das Taxi jetzt selbst. Nutze /stoptaxi um es zu beenden.', type = 'info' }) end function ExitTaxi() print("^2[TAXI DEBUG]^7 Player exiting taxi") if not currentTaxi or not DoesEntityExist(currentTaxi) then return end local playerPed = PlayerPedId() TaskLeaveVehicle(playerPed, currentTaxi, 0) lib.notify({ title = 'Taxi Service', description = 'Du bist ausgestiegen', type = 'info' }) -- Taxi nach 5 Sekunden despawnen SetTimeout(5000, function() DespawnTaxi() end) end function DespawnTaxi() print("^2[TAXI DEBUG]^7 Despawning taxi") if not currentTaxi or not DoesEntityExist(currentTaxi) then return end -- Taxi wegfahren lassen, wenn ein Fahrer existiert if currentDriver and DoesEntityExist(currentDriver) then print("^2[TAXI DEBUG]^7 Making taxi drive away before despawn") -- Zufällige Position in der Nähe finden local taxiCoords = GetEntityCoords(currentTaxi) local angle = math.random() * 2 * math.pi local distance = 150.0 local driveToX = taxiCoords.x + math.cos(angle) * distance local driveToY = taxiCoords.y + math.sin(angle) * distance -- Taxi wegfahren lassen TaskVehicleDriveToCoordLongrange(currentDriver, currentTaxi, driveToX, driveToY, taxiCoords.z, 25.0, 786603, 5.0) -- Blips entfernen if taxiBlip then RemoveBlip(taxiBlip) taxiBlip = nil end if mapBlip then RemoveBlip(mapBlip) mapBlip = nil end if destinationBlip then RemoveBlip(destinationBlip) destinationBlip = nil end -- Nach 10 Sekunden tatsächlich löschen SetTimeout(10000, function() -- Fahrer löschen if currentDriver and DoesEntityExist(currentDriver) then DeleteEntity(currentDriver) currentDriver = nil print("^2[TAXI DEBUG]^7 Driver deleted") end -- Taxi löschen if currentTaxi and DoesEntityExist(currentTaxi) then exports['qb-target']:RemoveTargetEntity(currentTaxi) DeleteEntity(currentTaxi) currentTaxi = nil print("^2[TAXI DEBUG]^7 Taxi deleted") end print("^2[TAXI DEBUG]^7 Taxi despawn completed") end) else -- Sofort löschen wenn kein Fahrer da ist if taxiBlip then RemoveBlip(taxiBlip) taxiBlip = nil end if mapBlip then RemoveBlip(mapBlip) mapBlip = nil end if destinationBlip then RemoveBlip(destinationBlip) destinationBlip = nil end -- Taxi löschen if currentTaxi and DoesEntityExist(currentTaxi) then exports['qb-target']:RemoveTargetEntity(currentTaxi) DeleteEntity(currentTaxi) currentTaxi = nil print("^2[TAXI DEBUG]^7 Taxi deleted") end print("^2[TAXI DEBUG]^7 Taxi despawn completed (no driver)") end end function CalculateDistanceToCoords(coords) local playerCoords = GetEntityCoords(PlayerPedId()) return #(playerCoords - coords) end -- Command um Taxi zu stoppen RegisterCommand('stoptaxi', function() if currentTaxi and DoesEntityExist(currentTaxi) then DespawnTaxi() lib.notify({ title = 'Taxi Service', description = 'Taxi-Service beendet', type = 'info' }) else lib.notify({ title = 'Taxi Service', description = 'Du hast kein aktives Taxi', type = 'error' }) end end) -- Thread zum Überwachen der Tasten im Taxi CreateThread(function() while true do Wait(0) if currentTaxi and DoesEntityExist(currentTaxi) then local playerPed = PlayerPedId() if IsPedInVehicle(playerPed, currentTaxi, false) 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 OpenDestinationMenu() end -- Wenn F gedrückt wird, beende Fahrt if IsControlJustReleased(0, 23) then -- F Taste lib.hideTextUI() EndTaxiRide() end else lib.hideTextUI() end else lib.hideTextUI() Wait(1000) -- Längere Wartezeit wenn kein Taxi existiert end end end) -- Funktion zum Beenden der Fahrt function EndTaxiRide() print("^2[TAXI DEBUG]^7 Ending taxi ride") if not currentTaxi or not DoesEntityExist(currentTaxi) 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, currentTaxi, 0) -- Warten bis ausgestiegen CreateThread(function() local timeout = GetGameTimer() + 5000 while GetGameTimer() < timeout do if not IsPedInVehicle(playerPed, currentTaxi, false) then -- Spieler ist ausgestiegen break end Wait(100) end -- Taxi nach 5 Sekunden despawnen SetTimeout(5000, function() DespawnTaxi() end) end) end -- Thread zum Überwachen des Einsteigens ins Taxi (ohne qb-target) CreateThread(function() while true do Wait(1000) if currentTaxi and DoesEntityExist(currentTaxi) and not IsPedInVehicle(PlayerPedId(), currentTaxi, false) then -- Spieler ist nicht im Taxi, aber Taxi existiert local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local taxiCoords = GetEntityCoords(currentTaxi) if #(playerCoords - taxiCoords) < 5.0 then -- Spieler ist in der Nähe des Taxis lib.showTextUI('[E] - Ins Taxi einsteigen', { position = "top-center", icon = 'car-side', style = { borderRadius = 10, backgroundColor = '#4299E1', color = 'white' } }) -- Prüfen ob E gedrückt wird if IsControlJustReleased(0, 38) then -- E Taste -- Spieler will einsteigen lib.hideTextUI() -- Spieler hinten einsteigen lassen local seatIndex = 1 -- Hinten links if not IsVehicleSeatFree(currentTaxi, 1) then seatIndex = 2 -- Hinten rechts end if not IsVehicleSeatFree(currentTaxi, seatIndex) then seatIndex = 0 -- Beifahrer als Fallback end TaskEnterVehicle(playerPed, currentTaxi, 10000, seatIndex, 1.0, 1, 0) -- Warten bis eingestiegen local entryTimeout = GetGameTimer() + 10000 CreateThread(function() while GetGameTimer() < entryTimeout do if IsPedInVehicle(playerPed, currentTaxi, false) then -- Spieler ist eingestiegen Wait(1000) OpenDestinationMenu() break end Wait(100) end end) end else lib.hideTextUI() end end end end) -- Cleanup beim Resource Stop AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then print("^2[TAXI DEBUG]^7 Cleaning up main script...") -- UI ausblenden lib.hideTextUI() -- Taxi despawnen DespawnTaxi() print("^2[TAXI DEBUG]^7 Main cleanup completed") end end) -- Starte die Taxi-Überwachung function MonitorTaxiExistence() CreateThread(function() local lastKnownPosition = nil local lastKnownHeading = 0.0 while true do Wait(2000) -- Alle 2 Sekunden prüfen if currentTaxi ~= nil then if DoesEntityExist(currentTaxi) then -- Speichere aktuelle Position für Notfall-Respawn lastKnownPosition = GetEntityCoords(currentTaxi) lastKnownHeading = GetEntityHeading(currentTaxi) else -- Taxi existiert nicht mehr, aber sollte existieren print("^1[TAXI DEBUG]^7 Taxi disappeared unexpectedly, respawning...") -- Speichere wichtige Informationen local hadDriver = (currentDriver ~= nil and DoesEntityExist(currentDriver)) local playerInTaxi = false local playerPed = PlayerPedId() -- Altes Taxi und Fahrer aufräumen if currentDriver and DoesEntityExist(currentDriver) then DeleteEntity(currentDriver) end currentDriver = nil -- Bestimme Respawn-Position local respawnPos if lastKnownPosition then respawnPos = { x = lastKnownPosition.x, y = lastKnownPosition.y, z = lastKnownPosition.z, w = lastKnownHeading } else -- Fallback: In der Nähe des Spielers spawnen local playerCoords = GetEntityCoords(playerPed) respawnPos = { x = playerCoords.x + math.random(-10, 10), y = playerCoords.y + math.random(-10, 10), z = playerCoords.z, w = 0.0 } end -- Neues Taxi spawnen local newTaxi = SpawnTaxi(respawnPos) if newTaxi then currentTaxi = newTaxi -- Neuen Fahrer spawnen wenn nötig if hadDriver then local newDriver = SpawnTaxiDriver(newTaxi) if newDriver then currentDriver = newDriver -- Navigation fortsetzen wenn nötig local playerCoords = GetEntityCoords(playerPed) NavigateToPlayer(currentDriver, currentTaxi, playerCoords) end end -- Blips neu erstellen CreateTaxiBlips(newTaxi) lib.notify({ title = 'Taxi Service', description = 'Dein Taxi wurde ersetzt.', type = 'info' }) end end end end end) end