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(2000) -- Längere Wartezeit print("^2[TAXI STATIONS DEBUG]^7 Initializing taxi stations...") InitializeTaxiStations() 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") 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] = {} for vehicleId, vehicleData in pairs(station.vehicles) do print("^2[TAXI STATIONS DEBUG]^7 Spawning vehicle " .. vehicleId .. " (" .. vehicleData.model .. ") at station " .. stationId) SpawnStationVehicle(stationId, vehicleId, vehicleData) end end print("^2[TAXI STATIONS DEBUG]^7 All stations initialized") end function SpawnStationVehicle(stationId, vehicleId, vehicleData) print("^2[TAXI STATIONS DEBUG]^7 SpawnStationVehicle: " .. stationId .. "/" .. vehicleId) 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) 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!") return end print("^2[TAXI STATIONS DEBUG]^7 Vehicle created: " .. vehicle) SetEntityAsMissionEntity(vehicle, true, true) SetVehicleOnGroundProperly(vehicle) SetVehicleDoorsLocked(vehicle, 2) -- Locked SetVehicleEngineOn(vehicle, false, true, false) -- Fahrzeug-Info speichern 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 -- 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) -- Fahrer spawnen local driverHash = GetHashKey("a_m_m_taxi_01") RequestModel(driverHash) local timeout = GetGameTimer() + 10000 while not HasModelLoaded(driverHash) and GetGameTimer() < timeout do print("^3[TAXI STATIONS DEBUG]^7 Waiting for driver model to load...") Wait(100) end if not HasModelLoaded(driverHash) then print("^1[TAXI STATIONS DEBUG]^7 Failed to load driver model!") return end local driver = CreatePedInsideVehicle(vehicle, 26, driverHash, -1, true, false) if not DoesEntityExist(driver) then print("^1[TAXI STATIONS DEBUG]^7 Failed to create driver!") return end print("^2[TAXI STATIONS DEBUG]^7 Driver created: " .. driver) 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) -- Spieler einsteigen lassen TaskEnterVehicle(playerPed, vehicle, 10000, 0, 1.0, 1, 0) -- Warten bis Spieler eingestiegen ist CreateThread(function() local enterTimeout = GetGameTimer() + 10000 while GetGameTimer() < enterTimeout do if IsPedInVehicle(playerPed, vehicle, false) then vehicleInfo.occupied = true vehicleInfo.driver = driver print("^2[TAXI STATIONS DEBUG]^7 Player entered, opening menu") -- Ziel-Menu öffnen OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, vehicleInfo.data.pricePerKm) break end Wait(100) end if not IsPedInVehicle(playerPed, vehicle, false) then print("^1[TAXI STATIONS DEBUG]^7 Player failed to enter vehicle") -- Cleanup if DoesEntityExist(driver) then DeleteEntity(driver) end SetVehicleDoorsLocked(vehicle, 2) 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 }) -- 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 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)) 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) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Taxi Ziel") EndTextCommandSetBlipName(destinationBlip) -- Zum Ziel fahren TaskVehicleDriveToCoord(driver, vehicle, destination.x, destination.y, destination.z, 25.0, 0, GetEntityModel(vehicle), 786603, 1.0, true) -- Fahrt überwachen CreateThread(function() while DoesEntityExist(vehicle) and DoesEntityExist(driver) do local vehicleCoords = GetEntityCoords(vehicle) local distance = #(vector3(destination.x, destination.y, destination.z) - vehicleCoords) if distance < 10.0 then -- Angekommen TaskVehicleTempAction(driver, vehicle, 27, 3000) print("^2[TAXI STATIONS DEBUG]^7 Arrived at destination") lib.notify({ title = 'Taxi Service', description = 'Du bist angekommen! Preis: $' .. price, type = 'success' }) -- Bezahlung TriggerServerEvent('taxi:payFare', price) -- Blip entfernen RemoveBlip(destinationBlip) -- Nach 10 Sekunden Taxi zurück zur Station SetTimeout(10000, function() ReturnTaxiToStation(stationId, vehicleId, vehicle, driver) end) break end 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 -- Fahrer löschen if DoesEntityExist(driver) then DeleteEntity(driver) print("^2[TAXI STATIONS DEBUG]^7 Driver deleted") end -- 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 function CalculateDistanceToCoords(coords) local playerCoords = GetEntityCoords(PlayerPedId()) return #(playerCoords - coords) end -- Zusätzliche Funktionen für bessere Verwaltung function GetNearestTaxiStation() 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 return nearestStation end -- Command um nächste Taxi-Station zu finden RegisterCommand('nearesttaxi', function() local nearest = GetNearestTaxiStation() if nearest then lib.notify({ title = 'Taxi Service', description = 'Nächste Station: ' .. nearest.data.name .. ' (' .. math.ceil(nearest.distance) .. 'm)', type = 'info' }) -- Waypoint zur nächsten Station setzen SetNewWaypoint(nearest.data.blipCoords.x, nearest.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 geladen', type = 'success' }) end) -- Verbesserte Fahrzeug-Respawn-Logik function RespawnStationVehicle(stationId, vehicleId) if not stationVehicles[stationId] or not stationVehicles[stationId][vehicleId] then return end local vehicleData = stationVehicles[stationId][vehicleId].data -- Prüfen ob Position frei ist local coords = vehicleData.coords local existingVehicles = GetGamePool('CVehicle') local positionBlocked = false for _, veh in pairs(existingVehicles) do local vehCoords = GetEntityCoords(veh) if #(vector3(coords.x, coords.y, coords.z) - vehCoords) < 3.0 then positionBlocked = true break end end if not positionBlocked then SpawnStationVehicle(stationId, vehicleId, vehicleData) else -- Erneut versuchen nach 30 Sekunden SetTimeout(30000, function() RespawnStationVehicle(stationId, vehicleId) end) end end -- Cleanup beim Resource Stop AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then print("^2[TAXI STATIONS DEBUG]^7 Cleaning up stations...") -- 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)