diff --git a/resources/[tools]/nordi_taxi/client/main.lua b/resources/[tools]/nordi_taxi/client/main.lua index ed039e5a9..24df570be 100644 --- a/resources/[tools]/nordi_taxi/client/main.lua +++ b/resources/[tools]/nordi_taxi/client/main.lua @@ -1,32 +1,22 @@ local QBCore = exports['qb-core']:GetCoreObject() local currentTaxi = nil -local taxiDriver = nil -local inTaxi = false +local currentDriver = nil local taxiBlip = nil local destinationBlip = nil -local meterRunning = false -local currentFare = 0 -local lastTaxiCall = 0 +local taxiMeter = { + isRunning = false, + startCoords = nil, + currentFare = 0, + pricePerKm = 0 +} -print("^2[TAXI DEBUG]^7 Client script loaded") +print("^2[TAXI DEBUG]^7 Main script loaded") --- Taxi rufen Command (Mobile Taxi) +-- Taxi rufen Command RegisterCommand('taxi', function() - print("^2[TAXI DEBUG]^7 /taxi command executed") + print("^2[TAXI DEBUG]^7 Taxi command executed") - local currentTime = GetGameTimer() - if currentTime - lastTaxiCall < (Config.TaxiCallCooldown * 1000) then - local remainingTime = math.ceil((Config.TaxiCallCooldown * 1000 - (currentTime - lastTaxiCall)) / 1000) - print("^1[TAXI DEBUG]^7 Cooldown active: " .. remainingTime .. " seconds") - lib.notify({ - title = 'Taxi Service', - description = 'Du musst noch ' .. remainingTime .. ' Sekunden warten', - type = 'error' - }) - return - end - - if currentTaxi then + if currentTaxi and DoesEntityExist(currentTaxi) then print("^1[TAXI DEBUG]^7 Taxi already exists") lib.notify({ title = 'Taxi Service', @@ -35,227 +25,407 @@ RegisterCommand('taxi', function() }) return end - - print("^2[TAXI DEBUG]^7 Calling mobile taxi...") - CallMobileTaxi() + + CallTaxi() end) -function CallMobileTaxi() - print("^2[TAXI DEBUG]^7 CallMobileTaxi function started") - lastTaxiCall = GetGameTimer() +function CallTaxi() + print("^2[TAXI DEBUG]^7 CallTaxi function started") lib.notify({ title = 'Taxi Service', - description = 'Ein Taxi wurde gerufen und ist auf dem Weg zu dir', - type = 'success' + description = 'Taxi wird gerufen...', + type = 'info' }) - + CreateThread(function() - print("^2[TAXI DEBUG]^7 Taxi spawn thread started") - local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) + print("^2[TAXI DEBUG]^7 Player coords: " .. tostring(playerCoords)) - -- Prüfe ob Config existiert - if not Config.MobileTaxiSpawns then - print("^1[TAXI DEBUG]^7 Config.MobileTaxiSpawns not found!") + -- Spawn-Position für Taxi finden + local spawnCoords = GetTaxiSpawnPosition(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 - -- Zufällige Spawn-Location wählen - local spawnLocation = Config.MobileTaxiSpawns[math.random(#Config.MobileTaxiSpawns)] - print("^2[TAXI DEBUG]^7 Spawn location: " .. tostring(spawnLocation)) + print("^2[TAXI DEBUG]^7 Spawn coords found: " .. tostring(spawnCoords)) - -- Zufälliges Taxi-Fahrzeug wählen - local selectedVehicle = SelectRandomTaxi() - print("^2[TAXI DEBUG]^7 Selected vehicle: " .. selectedVehicle.model) - - -- Fahrzeug spawnen - local vehicleHash = GetHashKey(selectedVehicle.model) - print("^2[TAXI DEBUG]^7 Vehicle hash: " .. vehicleHash) - - RequestModel(vehicleHash) - local timeout = GetGameTimer() + 10000 - while not HasModelLoaded(vehicleHash) and GetGameTimer() < timeout do - print("^3[TAXI DEBUG]^7 Waiting for model to load...") - Wait(100) - end - - if not HasModelLoaded(vehicleHash) then - print("^1[TAXI DEBUG]^7 Failed to load vehicle model!") - return - end - - print("^2[TAXI DEBUG]^7 Creating vehicle...") - currentTaxi = CreateVehicle(vehicleHash, spawnLocation.x, spawnLocation.y, spawnLocation.z, spawnLocation.w, true, false) - - if not DoesEntityExist(currentTaxi) then - print("^1[TAXI DEBUG]^7 Failed to create vehicle!") + -- 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 Vehicle created successfully: " .. currentTaxi) - SetEntityAsMissionEntity(currentTaxi, true, true) - SetVehicleOnGroundProperly(currentTaxi) + print("^2[TAXI DEBUG]^7 Taxi spawned: " .. taxi) + currentTaxi = taxi -- Fahrer spawnen - local driverHash = GetHashKey("a_m_m_taxi_01") + local driver = SpawnTaxiDriver(taxi) + if driver then + currentDriver = driver + print("^2[TAXI DEBUG]^7 Driver spawned: " .. driver) + + -- Zum Spieler fahren + TaskVehicleDriveToCoord(driver, taxi, playerCoords.x, playerCoords.y, playerCoords.z, 25.0, 0, GetEntityModel(taxi), 786603, 1.0, true) + + -- Blip für Taxi erstellen + taxiBlip = AddBlipForEntity(taxi) + SetBlipSprite(taxiBlip, 198) + SetBlipColour(taxiBlip, 5) + SetBlipScale(taxiBlip, 0.8) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString("Dein Taxi") + EndTextCommandSetBlipName(taxiBlip) + + lib.notify({ + title = 'Taxi Service', + description = 'Taxi ist unterwegs zu dir!', + type = 'success' + }) + + -- Überwachung der Ankunft + MonitorTaxiArrival(taxi, driver, playerCoords) + 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 GetTaxiSpawnPosition(playerCoords) + print("^2[TAXI DEBUG]^7 Finding spawn position...") + + local spawnDistance = 100.0 + local maxAttempts = 10 + + for i = 1, maxAttempts do + local angle = math.random() * 2 * math.pi + local x = playerCoords.x + math.cos(angle) * spawnDistance + local y = playerCoords.y + math.sin(angle) * spawnDistance + + local groundZ = 0.0 + local foundGround, z = GetGroundZFor_3dCoord(x, y, playerCoords.z + 50.0, groundZ, false) + + if foundGround then + local spawnCoords = vector4(x, y, z, math.deg(angle)) + + -- Prüfen ob Position frei ist + if IsPositionClear(spawnCoords.x, spawnCoords.y, spawnCoords.z, 3.0, true, false, false, false, false) then + print("^2[TAXI DEBUG]^7 Found spawn position at attempt " .. i) + return spawnCoords + end + end + + spawnDistance = spawnDistance + 20.0 + end + + print("^1[TAXI DEBUG]^7 No spawn position found after " .. maxAttempts .. " attempts") + return nil +end + +function SpawnTaxi(coords) + print("^2[TAXI DEBUG]^7 Spawning taxi at: " .. tostring(coords)) + + local taxiModel = GetHashKey(Config.TaxiModel) + print("^2[TAXI DEBUG]^7 Taxi model hash: " .. taxiModel) + + RequestModel(taxiModel) + local timeout = GetGameTimer() + 10000 + while not HasModelLoaded(taxiModel) and GetGameTimer() < timeout do + print("^3[TAXI DEBUG]^7 Waiting for taxi model to load...") + Wait(100) + end + + if not HasModelLoaded(taxiModel) then + print("^1[TAXI DEBUG]^7 Failed to load taxi model!") + return nil + end + + local taxi = CreateVehicle(taxiModel, coords.x, coords.y, coords.z, coords.w, 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) + + SetEntityAsMissionEntity(taxi, true, 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 + + SetModelAsNoLongerNeeded(taxiModel) + return taxi +end + +function SpawnTaxiDriver(vehicle) + print("^2[TAXI DEBUG]^7 Spawning taxi driver...") + + -- Bessere Fahrer-Models mit Fallbacks + local driverModels = { + "mp_m_freemode_01", -- Standard Male (sollte immer verfügbar sein) + "mp_f_freemode_01", -- Standard Female + "a_m_y_business_01", -- Business Male + "a_f_y_business_01", -- Business Female + "a_m_m_business_01", -- Business Male 2 + "a_m_y_downtown_01", -- Downtown Male + "s_m_m_pilot_01", -- Pilot + "s_m_y_dealer_01" -- Dealer + } + + local driver = nil + local driverHash = nil + + -- Versuche verschiedene Models + for i, modelName in pairs(driverModels) do + print("^2[TAXI DEBUG]^7 Trying driver model " .. i .. ": " .. modelName) + driverHash = GetHashKey(modelName) + + -- Model laden RequestModel(driverHash) - timeout = GetGameTimer() + 10000 + local timeout = GetGameTimer() + 8000 -- Längere Wartezeit + local attempts = 0 + while not HasModelLoaded(driverHash) and GetGameTimer() < timeout do - print("^3[TAXI DEBUG]^7 Waiting for driver model to load...") + 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 not HasModelLoaded(driverHash) then - print("^1[TAXI DEBUG]^7 Failed to load driver model!") - DeleteEntity(currentTaxi) - currentTaxi = nil - return - end - - print("^2[TAXI DEBUG]^7 Creating driver...") - taxiDriver = CreatePedInsideVehicle(currentTaxi, 26, driverHash, -1, true, false) - - if not DoesEntityExist(taxiDriver) then - print("^1[TAXI DEBUG]^7 Failed to create driver!") - DeleteEntity(currentTaxi) - currentTaxi = nil - return - end - - print("^2[TAXI DEBUG]^7 Driver created successfully: " .. taxiDriver) - SetEntityAsMissionEntity(taxiDriver, true, true) - SetPedFleeAttributes(taxiDriver, 0, 0) - SetPedCombatAttributes(taxiDriver, 17, 1) - SetPedSeeingRange(taxiDriver, 0.0) - SetPedHearingRange(taxiDriver, 0.0) - SetPedAlertness(taxiDriver, 0) - SetPedKeepTask(taxiDriver, true) - - -- Blip erstellen - taxiBlip = AddBlipForEntity(currentTaxi) - SetBlipSprite(taxiBlip, 198) - SetBlipColour(taxiBlip, 5) - SetBlipScale(taxiBlip, 0.8) - BeginTextCommandSetBlipName("STRING") - AddTextComponentString("Taxi") - EndTextCommandSetBlipName(taxiBlip) - print("^2[TAXI DEBUG]^7 Blip created") - - -- Zum Spieler fahren - print("^2[TAXI DEBUG]^7 Sending taxi to player...") - TaskVehicleDriveToCoord(taxiDriver, currentTaxi, playerCoords.x, playerCoords.y, playerCoords.z, 20.0, 0, vehicleHash, 786603, 1.0, true) - - -- Warten bis Taxi ankommt - local arrived = false - timeout = GetGameTimer() + (Config.MaxWaitTime * 1000) - - while not arrived and GetGameTimer() < timeout do - if not DoesEntityExist(currentTaxi) or not DoesEntityExist(taxiDriver) then - print("^1[TAXI DEBUG]^7 Taxi or driver disappeared!") + 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 All driver models failed, trying emergency fallback...") + + -- Notfall-Fallback: Versuche den einfachsten Ped zu erstellen + 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 - local taxiCoords = GetEntityCoords(currentTaxi) + if HasModelLoaded(hash) then + driver = CreatePedInsideVehicle(vehicle, 26, hash, -1, true, false) + if DoesEntityExist(driver) then + print("^2[TAXI DEBUG]^7 Emergency driver created with hash: " .. hash) + driverHash = hash + break + end + SetModelAsNoLongerNeeded(hash) + end + end + end + + -- Wenn immer noch kein Fahrer + 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) + 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) + + -- Fahrer-Outfit (nur wenn es ein anpassbarer Ped ist) + if driverHash == GetHashKey("mp_m_freemode_01") or driverHash == GetHashKey("mp_f_freemode_01") then + print("^2[TAXI DEBUG]^7 Setting driver outfit...") + + -- Basis-Outfit für Taxi-Fahrer + SetPedComponentVariation(driver, 8, 15, 0, 0) -- Undershirt + SetPedComponentVariation(driver, 11, 28, 0, 0) -- Jacket + SetPedComponentVariation(driver, 4, 10, 0, 0) -- Pants + SetPedComponentVariation(driver, 6, 10, 0, 0) -- Shoes + SetPedComponentVariation(driver, 1, 0, 0, 0) -- Mask + SetPedComponentVariation(driver, 3, 0, 0, 0) -- Arms + SetPedComponentVariation(driver, 5, 0, 0, 0) -- Bag + SetPedComponentVariation(driver, 7, 0, 0, 0) -- Tie + SetPedComponentVariation(driver, 9, 0, 0, 0) -- Body Armor + SetPedComponentVariation(driver, 10, 0, 0, 0) -- Decals + + -- Zufällige Gesichtsmerkmale + SetPedHeadBlendData(driver, math.random(0, 20), math.random(0, 20), 0, math.random(0, 20), math.random(0, 20), 0, 0.5, 0.5, 0.0, false) + end + + -- Model nicht mehr benötigt + if driverHash then + SetModelAsNoLongerNeeded(driverHash) + end + + print("^2[TAXI DEBUG]^7 Driver spawn completed successfully") + return driver +end + +function MonitorTaxiArrival(taxi, driver, playerCoords) + print("^2[TAXI DEBUG]^7 Monitoring taxi arrival...") + + CreateThread(function() + while DoesEntityExist(taxi) and (not driver or DoesEntityExist(driver)) do + local taxiCoords = GetEntityCoords(taxi) local distance = #(playerCoords - taxiCoords) - if distance < 10.0 then - arrived = true - TaskVehicleTempAction(taxiDriver, currentTaxi, 27, 3000) - + 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' }) - WaitForPlayerToEnter(selectedVehicle.pricePerKm) - end - Wait(1000) - end - - if not arrived then - print("^1[TAXI DEBUG]^7 Taxi failed to arrive in time") - lib.notify({ - title = 'Taxi Service', - description = 'Das Taxi konnte dich nicht erreichen', - type = 'error' - }) - CleanupMobileTaxi() - end - end) -end - -function SelectRandomTaxi() - if not Config.TaxiVehicles or #Config.TaxiVehicles == 0 then - print("^1[TAXI DEBUG]^7 No taxi vehicles configured!") - return {model = 'taxi', pricePerKm = 5} - end - - local totalChance = 0 - for _, vehicle in pairs(Config.TaxiVehicles) do - totalChance = totalChance + vehicle.spawnChance - end - - local randomValue = math.random(1, totalChance) - local currentChance = 0 - - for _, vehicle in pairs(Config.TaxiVehicles) do - currentChance = currentChance + vehicle.spawnChance - if randomValue <= currentChance then - return vehicle - end - end - - return Config.TaxiVehicles[1] -end - -function WaitForPlayerToEnter(pricePerKm) - print("^2[TAXI DEBUG]^7 Waiting for player to enter...") - CreateThread(function() - local timeout = GetGameTimer() + 60000 - - while currentTaxi and GetGameTimer() < timeout do - local playerPed = PlayerPedId() - - if IsPedInVehicle(playerPed, currentTaxi, false) then - inTaxi = true - print("^2[TAXI DEBUG]^7 Player entered taxi") - OpenMobileTaxiMenu(pricePerKm) + -- 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 - Wait(1000) - end - - if not inTaxi then - print("^1[TAXI DEBUG]^7 Player didn't enter taxi in time") - lib.notify({ - title = 'Taxi Service', - description = 'Das Taxi ist weggefahren', - type = 'error' - }) - CleanupMobileTaxi() + Wait(2000) end end) end -function OpenMobileTaxiMenu(pricePerKm) - print("^2[TAXI DEBUG]^7 Opening mobile taxi menu") +-- 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 + while GetGameTimer() < timeout do + if IsPedInVehicle(playerPed, currentTaxi, false) then + 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() + break + end + Wait(100) + 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 customPrice = math.max(Config.MinFare, math.ceil((CalculateDistance(destination.coords) / 1000) * pricePerKm)) + 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: $' .. customPrice, + description = 'Preis: $' .. price .. ' | Entfernung: ' .. math.ceil(distance * 100) / 100 .. 'km', icon = 'map-marker', onSelect = function() - StartMobileTaxiRide(destination.coords, customPrice) + StartTaxiRide(destination.coords, price) end }) end @@ -269,48 +439,74 @@ function OpenMobileTaxiMenu(pricePerKm) local waypoint = GetFirstBlipInfoId(8) if DoesBlipExist(waypoint) then local coords = GetBlipInfoIdCoord(waypoint) - local distance = CalculateDistance(coords) / 1000 - local price = math.max(Config.MinFare, math.ceil(distance * pricePerKm)) - StartMobileTaxiRide(coords, price) + 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' }) - OpenMobileTaxiMenu(pricePerKm) + 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() - ExitMobileTaxi() + ExitTaxi() end }) lib.registerContext({ - id = 'mobile_taxi_menu', + id = 'taxi_destination_menu', title = 'Taxi - Ziel wählen', options = options }) - lib.showContext('mobile_taxi_menu') + lib.showContext('taxi_destination_menu') end -function StartMobileTaxiRide(destination, price) - if not currentTaxi or not taxiDriver then - print("^1[TAXI DEBUG]^7 No taxi or driver for ride") - return +function StartTaxiRide(destination, price) + print("^2[TAXI DEBUG]^7 Starting taxi ride to: " .. tostring(destination)) + + if not currentTaxi or not DoesEntityExist(currentTaxi) then + print("^1[TAXI DEBUG]^7 No taxi exists for ride") + return end - print("^2[TAXI DEBUG]^7 Starting taxi ride to: " .. tostring(destination)) - currentFare = price - meterRunning = true + -- 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 destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z) @@ -321,35 +517,44 @@ function StartMobileTaxiRide(destination, price) AddTextComponentString("Taxi Ziel") EndTextCommandSetBlipName(destinationBlip) - lib.notify({ - title = 'Taxi Service', - description = 'Fahrt gestartet - Preis: $' .. price, - type = 'success' - }) - -- Zum Ziel fahren - TaskVehicleDriveToCoord(taxiDriver, currentTaxi, destination.x, destination.y, destination.z, 25.0, 0, GetEntityModel(currentTaxi), 786603, 1.0, true) + TaskVehicleDriveToCoord(currentDriver, currentTaxi, destination.x, destination.y, destination.z, 25.0, 0, GetEntityModel(currentTaxi), 786603, 1.0, true) + + -- Fahrt überwachen + MonitorTaxiRide(destination, price) +end + +function MonitorTaxiRide(destination, price) + print("^2[TAXI DEBUG]^7 Monitoring taxi ride...") - -- Überwachen der Fahrt CreateThread(function() - while meterRunning and currentTaxi do + while DoesEntityExist(currentTaxi) and DoesEntityExist(currentDriver) do local taxiCoords = GetEntityCoords(currentTaxi) local distance = #(vector3(destination.x, destination.y, destination.z) - taxiCoords) if distance < 10.0 then - TaskVehicleTempAction(taxiDriver, currentTaxi, 27, 3000) + -- Angekommen + TaskVehicleTempAction(currentDriver, currentTaxi, 27, 3000) print("^2[TAXI DEBUG]^7 Arrived at destination") lib.notify({ title = 'Taxi Service', - description = 'Du bist angekommen! Preis: $' .. currentFare, + description = 'Du bist angekommen! Preis: $' .. price, type = 'success' }) - TriggerServerEvent('taxi:payFare', currentFare) + -- Bezahlung + TriggerServerEvent('taxi:payFare', price) + -- Blips entfernen + if destinationBlip then + RemoveBlip(destinationBlip) + destinationBlip = nil + end + + -- Nach 10 Sekunden Taxi despawnen SetTimeout(10000, function() - CleanupMobileTaxi() + DespawnTaxi() end) break @@ -360,13 +565,38 @@ function StartMobileTaxiRide(destination, price) end) end -function CalculateDistance(coords) - local playerCoords = GetEntityCoords(PlayerPedId()) - return #(playerCoords - coords) +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 ExitMobileTaxi() +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) @@ -376,16 +606,31 @@ function ExitMobileTaxi() type = 'info' }) + -- Taxi nach 5 Sekunden despawnen SetTimeout(5000, function() - CleanupMobileTaxi() + DespawnTaxi() end) end -function CleanupMobileTaxi() - print("^2[TAXI DEBUG]^7 Cleaning up mobile taxi") - meterRunning = false - inTaxi = false +function DespawnTaxi() + print("^2[TAXI DEBUG]^7 Despawning taxi") + -- 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 + + -- Blips entfernen if taxiBlip then RemoveBlip(taxiBlip) taxiBlip = nil @@ -396,48 +641,37 @@ function CleanupMobileTaxi() destinationBlip = nil end - if currentTaxi then - if taxiDriver then - DeleteEntity(taxiDriver) - taxiDriver = nil - end - DeleteEntity(currentTaxi) - currentTaxi = nil - end + print("^2[TAXI DEBUG]^7 Taxi despawn completed") end --- Menu erneut öffnen wenn im Mobile Taxi -CreateThread(function() - while true do - if inTaxi and currentTaxi and not meterRunning then - if IsControlJustPressed(0, 38) then -- E Taste - local selectedVehicle = nil - local vehicleModel = GetEntityModel(currentTaxi) - - for _, vehicle in pairs(Config.TaxiVehicles) do - if GetHashKey(vehicle.model) == vehicleModel then - selectedVehicle = vehicle - break - end - end - - if selectedVehicle then - OpenMobileTaxiMenu(selectedVehicle.pricePerKm) - end - end - - lib.showTextUI('[E] - Ziel wählen') - else - lib.hideTextUI() - end - - Wait(0) +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) --- Cleanup beim Disconnect +-- Cleanup beim Resource Stop AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then - CleanupMobileTaxi() + print("^2[TAXI DEBUG]^7 Cleaning up main script...") + DespawnTaxi() + print("^2[TAXI DEBUG]^7 Main cleanup completed") end end) diff --git a/resources/[tools]/nordi_taxi/client/stations.lua b/resources/[tools]/nordi_taxi/client/stations.lua index b2ed6fc33..c3d738b06 100644 --- a/resources/[tools]/nordi_taxi/client/stations.lua +++ b/resources/[tools]/nordi_taxi/client/stations.lua @@ -190,22 +190,25 @@ RegisterNetEvent('taxi:enterStationVehicle', function(data) -- Verschiedene Fahrer-Models versuchen local driverModels = { - "a_m_m_taxi_01", - "s_m_y_taxi_01", - "a_m_y_business_01", - "a_m_m_business_01", - "a_m_y_downtown_01" + "mp_m_freemode_01", -- Standard Male (sollte immer verfügbar sein) + "mp_f_freemode_01", -- Standard Female + "a_m_y_business_01", -- Business Male + "a_f_y_business_01", -- Business Female + "a_m_m_business_01", -- Business Male 2 + "a_m_y_downtown_01", -- Downtown Male + "s_m_m_pilot_01", -- Pilot + "s_m_y_dealer_01" -- Dealer } local driver = nil local driverHash = nil - for _, modelName in pairs(driverModels) do - print("^2[TAXI STATIONS DEBUG]^7 Trying driver model: " .. modelName) + 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: ' .. modelName .. '...', { + lib.showTextUI('🚕 Lade Fahrer-Model (' .. i .. '/' .. #driverModels .. '): ' .. modelName .. '...', { position = "top-center", icon = 'taxi', style = { @@ -216,9 +219,14 @@ RegisterNetEvent('taxi:enterStationVehicle', function(data) }) RequestModel(driverHash) - local timeout = GetGameTimer() + 5000 + local timeout = GetGameTimer() + 8000 -- Längere Wartezeit + local attempts = 0 + while not HasModelLoaded(driverHash) and GetGameTimer() < timeout do - print("^3[TAXI STATIONS DEBUG]^7 Waiting for driver model " .. modelName .. " to load...") + 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 @@ -247,16 +255,17 @@ RegisterNetEvent('taxi:enterStationVehicle', function(data) 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: Fahrer ohne Model erstellen + -- Fallback: Notfall-Fahrer erstellen if not driver or not DoesEntityExist(driver) then - print("^3[TAXI STATIONS DEBUG]^7 Using fallback driver creation...") + print("^3[TAXI STATIONS DEBUG]^7 Using emergency fallback driver creation...") - lib.showTextUI('🚕 Lade Standard-Fahrer...', { + lib.showTextUI('🚕 Erstelle Notfall-Fahrer...', { position = "top-center", icon = 'taxi', style = { @@ -266,20 +275,33 @@ RegisterNetEvent('taxi:enterStationVehicle', function(data) } }) - -- Standard Male Model verwenden - driverHash = GetHashKey("mp_m_freemode_01") - RequestModel(driverHash) - local timeout = GetGameTimer() + 5000 - while not HasModelLoaded(driverHash) and GetGameTimer() < timeout do - Wait(100) + -- 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 - if HasModelLoaded(driverHash) then - driver = CreatePedInsideVehicle(vehicle, 26, driverHash, -1, true, false) - print("^2[TAXI STATIONS DEBUG]^7 Fallback driver created: " .. (driver or "nil")) - end - - Wait(1000) -- Länger warten für Fallback + Wait(1000) end -- Wenn immer noch kein Fahrer, ohne Fahrer fortfahren @@ -328,12 +350,27 @@ RegisterNetEvent('taxi:enterStationVehicle', function(data) SetPedHearingRange(driver, 0.0) SetPedAlertness(driver, 0) SetPedKeepTask(driver, true) + SetBlockingOfNonTemporaryEvents(driver, true) - -- Fahrer-Outfit für Taxi - SetPedComponentVariation(driver, 8, 0, 0, 0) -- Shirt - SetPedComponentVariation(driver, 11, 0, 0, 0) -- Jacket - SetPedComponentVariation(driver, 4, 0, 0, 0) -- Pants - SetPedComponentVariation(driver, 6, 0, 0, 0) -- Shoes + -- Fahrer-Outfit (nur wenn es ein anpassbarer Ped ist) + if driverHash == GetHashKey("mp_m_freemode_01") or driverHash == GetHashKey("mp_f_freemode_01") then + print("^2[TAXI STATIONS DEBUG]^7 Setting driver outfit...") + + -- Basis-Outfit für Taxi-Fahrer + SetPedComponentVariation(driver, 8, 15, 0, 0) -- Undershirt + SetPedComponentVariation(driver, 11, 28, 0, 0) -- Jacket + SetPedComponentVariation(driver, 4, 10, 0, 0) -- Pants + SetPedComponentVariation(driver, 6, 10, 0, 0) -- Shoes + SetPedComponentVariation(driver, 1, 0, 0, 0) -- Mask + SetPedComponentVariation(driver, 3, 0, 0, 0) -- Arms + SetPedComponentVariation(driver, 5, 0, 0, 0) -- Bag + SetPedComponentVariation(driver, 7, 0, 0, 0) -- Tie + SetPedComponentVariation(driver, 9, 0, 0, 0) -- Body Armor + SetPedComponentVariation(driver, 10, 0, 0, 0) -- Decals + + -- Zufällige Gesichtsmerkmale + SetPedHeadBlendData(driver, math.random(0, 20), math.random(0, 20), 0, math.random(0, 20), math.random(0, 20), 0, 0.5, 0.5, 0.0, false) + end lib.notify({ title = 'Taxi Service', @@ -444,7 +481,6 @@ RegisterNetEvent('taxi:enterStationVehicle', function(data) end) end) - function OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm) print("^2[TAXI STATIONS DEBUG]^7 Opening station taxi menu") @@ -805,8 +841,9 @@ RegisterNetEvent('taxi:respawnAllStations', function() InitializeTaxiStations() lib.notify({ + title = title = 'Taxi Service', - description = 'Alle Taxi-Stationen wurden neu geladen', + description = 'Alle Taxi-Stationen wurden neu gespawnt', type = 'success' }) end) @@ -840,4 +877,3 @@ AddEventHandler('onResourceStop', function(resourceName) print("^2[TAXI STATIONS DEBUG]^7 Cleanup completed") end end) -