1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_taxi/client/main.lua
2025-07-30 06:47:27 +02:00

1366 lines
48 KiB
Lua

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