1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_taxi/client/main.lua

1579 lines
59 KiB
Lua
Raw Normal View History

2025-07-30 02:17:38 +02:00
local QBCore = exports['qb-core']:GetCoreObject()
local currentTaxi = nil
2025-07-30 03:21:38 +02:00
local currentDriver = nil
2025-07-30 02:17:38 +02:00
local taxiBlip = nil
2025-07-30 04:08:22 +02:00
local mapBlip = nil
2025-07-30 02:17:38 +02:00
local destinationBlip = nil
2025-07-30 03:21:38 +02:00
local taxiMeter = {
isRunning = false,
startCoords = nil,
currentFare = 0,
pricePerKm = 0
}
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
print("^2[TAXI DEBUG]^7 Main script loaded")
2025-07-30 02:46:15 +02:00
2025-07-30 03:21:38 +02:00
-- Taxi rufen Command
2025-07-30 02:17:38 +02:00
RegisterCommand('taxi', function()
2025-07-30 03:21:38 +02:00
print("^2[TAXI DEBUG]^7 Taxi command executed")
2025-07-30 02:46:15 +02:00
2025-07-30 03:21:38 +02:00
if currentTaxi and DoesEntityExist(currentTaxi) then
2025-07-30 02:46:15 +02:00
print("^1[TAXI DEBUG]^7 Taxi already exists")
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Du hast bereits ein Taxi gerufen',
type = 'error'
})
return
end
2025-07-30 03:21:38 +02:00
CallTaxi()
2025-07-30 02:17:38 +02:00
end)
2025-07-30 03:21:38 +02:00
function CallTaxi()
print("^2[TAXI DEBUG]^7 CallTaxi function started")
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
2025-07-30 03:21:38 +02:00
description = 'Taxi wird gerufen...',
type = 'info'
2025-07-30 02:17:38 +02:00
})
2025-07-30 03:21:38 +02:00
2025-07-30 02:17:38 +02:00
CreateThread(function()
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
2025-07-30 03:21:38 +02:00
2025-07-30 02:46:15 +02:00
print("^2[TAXI DEBUG]^7 Player coords: " .. tostring(playerCoords))
2025-07-30 05:48:24 +02:00
-- Verbesserte Spawn-Position für Taxi finden
local spawnCoords = GetImprovedTaxiSpawnPosition(playerCoords)
2025-07-30 03:21:38 +02:00
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'
})
2025-07-30 02:46:15 +02:00
return
end
2025-07-30 02:17:38 +02:00
2025-07-30 05:48:24 +02:00
print("^2[TAXI DEBUG]^7 Spawn coords found: " .. tostring(spawnCoords.x) .. ", " .. tostring(spawnCoords.y) .. ", " .. tostring(spawnCoords.z))
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
-- 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
2025-07-30 02:17:38 +02:00
end
2025-07-30 02:46:15 +02:00
2025-07-30 03:21:38 +02:00
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)
2025-07-30 05:48:24 +02:00
-- Verbesserte Navigation zum Spieler
NavigateToPlayer(driver, taxi, playerCoords)
2025-07-30 03:21:38 +02:00
2025-07-30 04:08:22 +02:00
-- Blip für Taxi erstellen (Entity-Blip)
2025-07-30 05:48:24 +02:00
CreateTaxiBlips(taxi)
2025-07-30 03:21:38 +02:00
2025-07-30 05:48:24 +02:00
-- Überwachung der Ankunft
MonitorTaxiArrival(taxi, driver, playerCoords)
2025-07-30 04:08:22 +02:00
2025-07-30 03:21:38 +02:00
lib.notify({
title = 'Taxi Service',
2025-07-30 04:08:22 +02:00
description = 'Taxi ist unterwegs zu dir! Verfolge es auf der Karte.',
2025-07-30 03:21:38 +02:00
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'
})
2025-07-30 02:46:15 +02:00
end
2025-07-30 03:21:38 +02:00
end)
end
2025-07-30 02:17:38 +02:00
2025-07-30 05:48:24 +02:00
function GetImprovedTaxiSpawnPosition(playerCoords)
2025-07-30 06:50:33 +02:00
print("^2[TAXI DEBUG]^7 Finding improved spawn position...")
2025-07-30 09:27:31 +02:00
-- Minimale und maximale Entfernung zum Spieler
local minAcceptableDistance = 50.0 -- Mindestens 50 Meter entfernt
local maxAcceptableDistance = 150.0 -- Maximal 150 Meter entfernt
2025-07-30 06:50:33 +02:00
local bestPosition = nil
local bestDistance = 999999.0
-- Versuche zuerst einen Straßenknotenpunkt zu finden
local roadPosition = nil
local foundNode = false
local nodePos = vector3(0.0, 0.0, 0.0)
-- Versuche einen Straßenknotenpunkt in optimaler Entfernung zu finden
2025-07-30 09:27:31 +02:00
-- Suche in einem größeren Radius, um mehr Optionen zu haben
2025-07-30 06:50:33 +02:00
foundNode, nodePos = GetClosestVehicleNode(playerCoords.x, playerCoords.y, playerCoords.z, 1, 3.0, 0)
if foundNode then
local nodeDistance = #(playerCoords - nodePos)
2025-07-30 09:27:31 +02:00
if nodeDistance >= minAcceptableDistance and nodeDistance <= maxAcceptableDistance then
2025-07-30 06:50:33 +02:00
roadPosition = nodePos
print("^2[TAXI DEBUG]^7 Found road node for spawn at distance: " .. nodeDistance)
return {x = roadPosition.x, y = roadPosition.y, z = roadPosition.z, w = 0.0}
else
-- Speichern für später, falls wir nichts Besseres finden
2025-07-30 09:27:31 +02:00
if nodeDistance < bestDistance and nodeDistance >= minAcceptableDistance then
2025-07-30 06:50:33 +02:00
bestDistance = nodeDistance
bestPosition = {x = nodePos.x, y = nodePos.y, z = nodePos.z, w = 0.0}
end
end
end
2025-07-30 09:27:31 +02:00
-- Versuche mehrere Knotenpunkte in verschiedenen Richtungen zu finden
for i = 1, 8 do
local angle = (i - 1) * 45.0 -- 8 Richtungen (0, 45, 90, 135, 180, 225, 270, 315 Grad)
local searchDistance = (minAcceptableDistance + maxAcceptableDistance) / 2
local searchX = playerCoords.x + math.cos(math.rad(angle)) * searchDistance
local searchY = playerCoords.y + math.sin(math.rad(angle)) * searchDistance
foundNode, nodePos = GetClosestVehicleNodeWithHeading(searchX, searchY, playerCoords.z, 1, 3.0, 0)
if foundNode then
local nodeDistance = #(playerCoords - nodePos)
if nodeDistance >= minAcceptableDistance and nodeDistance <= maxAcceptableDistance then
print("^2[TAXI DEBUG]^7 Found directional node for spawn at distance: " .. nodeDistance .. " in direction " .. angle)
return {x = nodePos.x, y = nodePos.y, z = nodePos.z, w = 0.0}
else
-- Speichern für später, falls wir nichts Besseres finden
if nodeDistance < bestDistance and nodeDistance >= minAcceptableDistance then
bestDistance = nodeDistance
bestPosition = {x = nodePos.x, y = nodePos.y, z = nodePos.z, w = 0.0}
end
end
end
end
2025-07-30 06:50:33 +02:00
-- Versuche einen weiteren Knotenpunkt mit größerem Radius
foundNode, nodePos = GetClosestMajorVehicleNode(playerCoords.x, playerCoords.y, playerCoords.z, 100.0, 0)
if foundNode then
local nodeDistance = #(playerCoords - nodePos)
2025-07-30 09:27:31 +02:00
if nodeDistance >= minAcceptableDistance and nodeDistance <= maxAcceptableDistance then
2025-07-30 06:50:33 +02:00
roadPosition = nodePos
print("^2[TAXI DEBUG]^7 Found major road node for spawn at distance: " .. nodeDistance)
return {x = roadPosition.x, y = roadPosition.y, z = roadPosition.z, w = 0.0}
else
-- Speichern für später, falls wir nichts Besseres finden
2025-07-30 09:27:31 +02:00
if nodeDistance < bestDistance and nodeDistance >= minAcceptableDistance then
2025-07-30 06:50:33 +02:00
bestDistance = nodeDistance
bestPosition = {x = nodePos.x, y = nodePos.y, z = nodePos.z, w = 0.0}
end
end
2025-07-30 09:51:29 +02:00
end
2025-07-30 06:17:38 +02:00
2025-07-30 06:50:33 +02:00
-- Fallback auf Config-Positionen
2025-07-30 05:48:24 +02:00
if Config.MobileTaxiSpawns and #Config.MobileTaxiSpawns > 0 then
-- 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))
2025-07-30 09:27:31 +02:00
if distance >= minAcceptableDistance then
table.insert(sortedSpawns, {
coords = spawnPos,
distance = distance
})
end
2025-07-30 05:48:24 +02:00
end
2025-07-30 02:46:15 +02:00
2025-07-30 09:27:31 +02:00
-- Nach Entfernung sortieren (nächste zuerst, aber mindestens minAcceptableDistance entfernt)
2025-07-30 05:48:24 +02:00
table.sort(sortedSpawns, function(a, b)
return a.distance < b.distance
end)
2025-07-30 03:21:38 +02:00
2025-07-30 06:50:33 +02:00
-- Prüfen ob die nächsten Positionen frei und nah genug sind
2025-07-30 05:48:24 +02:00
for i, spawn in ipairs(sortedSpawns) do
local spawnCoords = spawn.coords
2025-07-30 06:50:33 +02:00
-- Wenn Position zu weit weg ist, überspringen
if spawn.distance > maxAcceptableDistance then
-- Speichern für später, falls wir nichts Besseres finden
if spawn.distance < bestDistance then
bestDistance = spawn.distance
bestPosition = spawnCoords
end
goto continue
end
2025-07-30 05:48:24 +02:00
-- 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
2025-07-30 03:21:38 +02:00
end
2025-07-30 06:50:33 +02:00
::continue::
2025-07-30 06:32:52 +02:00
end
end
2025-07-30 09:27:31 +02:00
-- Wenn wir hier sind, haben wir keine ideale Position gefunden
-- Wenn wir eine "beste" Position haben, die den Mindestabstand einhält, verwenden wir diese
if bestPosition and bestDistance >= minAcceptableDistance then
print("^3[TAXI DEBUG]^7 Using best available position at " .. bestDistance .. "m")
2025-07-30 06:50:33 +02:00
return bestPosition
2025-07-30 06:17:38 +02:00
end
2025-07-30 06:50:33 +02:00
-- Wenn alles fehlschlägt: Generiere eine zufällige Position in der Nähe des Spielers
2025-07-30 09:27:31 +02:00
print("^3[TAXI DEBUG]^7 Generating random position between " .. minAcceptableDistance .. "m and " .. maxAcceptableDistance .. "m of player")
2025-07-30 06:17:38 +02:00
2025-07-30 09:27:31 +02:00
-- Versuche bis zu 15 Mal, eine gültige Position zu finden
for attempt = 1, 15 do
-- Zufällige Position im Umkreis, aber mindestens minAcceptableDistance entfernt
2025-07-30 06:17:38 +02:00
local angle = math.random() * 2 * math.pi
2025-07-30 09:27:31 +02:00
local distance = math.random(minAcceptableDistance, maxAcceptableDistance)
2025-07-30 06:17:38 +02:00
local x = playerCoords.x + math.cos(angle) * distance
local y = playerCoords.y + math.sin(angle) * distance
local z = playerCoords.z
2025-07-30 02:46:15 +02:00
2025-07-30 06:17:38 +02:00
-- 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
2025-07-30 06:50:33 +02:00
print("^2[TAXI DEBUG]^7 Found random position on road at distance: " .. distance)
2025-07-30 06:17:38 +02:00
return {x = x, y = y, z = groundZ, w = 0.0}
end
end
2025-07-30 03:21:38 +02:00
end
2025-07-30 09:27:31 +02:00
-- Absolute Notfall-Fallback: Einfach irgendwo in der Nähe, aber mindestens minAcceptableDistance entfernt
2025-07-30 05:48:24 +02:00
local angle = math.random() * 2 * math.pi
2025-07-30 09:27:31 +02:00
local distance = math.random(minAcceptableDistance, maxAcceptableDistance)
2025-07-30 05:48:24 +02:00
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
2025-07-30 06:50:33 +02:00
print("^3[TAXI DEBUG]^7 Using emergency random spawn position at distance: " .. distance)
2025-07-30 05:48:24 +02:00
return {x = x, y = y, z = z, w = 0.0}
2025-07-30 03:21:38 +02:00
end
2025-07-30 09:27:31 +02:00
2025-07-30 03:50:38 +02:00
function SpawnTaxi(coords)
2025-07-30 05:48:24 +02:00
print("^2[TAXI DEBUG]^7 Spawning taxi at: " .. tostring(coords.x) .. ", " .. tostring(coords.y) .. ", " .. tostring(coords.z))
2025-07-30 03:50:38 +02:00
2025-07-30 04:08:22 +02:00
-- Sicherstellen dass wir ein gültiges Taxi-Model haben
local taxiModel = nil
if Config.TaxiVehicles and #Config.TaxiVehicles > 0 then
2025-07-30 05:48:24 +02:00
-- Zufälliges Taxi-Model aus Config wählen
local randomIndex = math.random(1, #Config.TaxiVehicles)
taxiModel = GetHashKey(Config.TaxiVehicles[randomIndex].model)
2025-07-30 04:08:22 +02:00
else
taxiModel = GetHashKey("taxi") -- Fallback
end
2025-07-30 03:50:38 +02:00
print("^2[TAXI DEBUG]^7 Taxi model hash: " .. taxiModel)
2025-07-30 05:48:24 +02:00
-- Model laden mit Timeout
2025-07-30 03:50:38 +02:00
RequestModel(taxiModel)
2025-07-30 05:48:24 +02:00
local modelLoaded = false
2025-07-30 03:50:38 +02:00
local timeout = GetGameTimer() + 10000
2025-07-30 05:48:24 +02:00
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
2025-07-30 03:50:38 +02:00
end
2025-07-30 05:48:24 +02:00
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
2025-07-30 03:50:38 +02:00
end
2025-07-30 05:48:24 +02:00
-- Fahrzeug erstellen
local taxi = CreateVehicle(taxiModel, coords.x, coords.y, coords.z, coords.w or 0.0, true, false)
2025-07-30 03:50:38 +02:00
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)
2025-07-30 05:48:24 +02:00
-- Fahrzeug konfigurieren
2025-07-30 03:50:38 +02:00
SetEntityAsMissionEntity(taxi, true, true)
SetVehicleOnGroundProperly(taxi)
SetVehicleEngineOn(taxi, true, true, false)
SetVehicleDoorsLocked(taxi, 2) -- Locked initially
2025-07-30 09:14:44 +02:00
-- Verbesserte Persistenz
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)
2025-07-30 03:50:38 +02:00
-- 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
2025-07-30 05:48:24 +02:00
-- 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
2025-07-30 03:50:38 +02:00
SetModelAsNoLongerNeeded(taxiModel)
return taxi
end
2025-07-30 03:38:41 +02:00
2025-07-30 09:14:44 +02:00
-- Fortgeschrittenes Taxi-Fahrer-Verhaltenssystem
function InitializeTaxiDriverAI(driver, vehicle)
if not driver or not DoesEntityExist(driver) then return end
print("^2[TAXI DEBUG]^7 Initializing advanced taxi driver AI")
-- Fahrer-Persönlichkeit und Fähigkeiten zufällig festlegen
local driverData = {
personality = {
patience = math.random(7, 10) / 10, -- 0.7-1.0: Wie geduldig ist der Fahrer
caution = math.random(6, 10) / 10, -- 0.6-1.0: Wie vorsichtig fährt er
speedPreference = math.random(15, 25), -- 15-25: Bevorzugte Geschwindigkeit
trafficRuleCompliance = math.random(8, 10)/10 -- 0.8-1.0: Wie genau hält er Verkehrsregeln ein
},
state = {
stuckCounter = 0,
lastStuckRecovery = 0,
lastRouteRecalculation = 0,
currentBehavior = "normal", -- normal, cautious, stuck, recovery
lastSpeedCheck = 0,
speedHistory = {},
lastPositions = {},
trafficLightWaitStart = 0,
isWaitingAtTrafficLight = false
},
settings = {
maxStuckCounter = math.random(15, 25), -- Wie lange warten bis Befreiungsversuch
stuckThreshold = 0.5, -- Bewegungsschwelle für Steckenbleiben
checkInterval = 2000, -- Überprüfungsintervall in ms
positionHistorySize = 5, -- Anzahl der zu speichernden Positionen
minRecoveryInterval = 25000, -- Min. Zeit zwischen Befreiungsversuchen
minRouteRecalcInterval = 30000, -- Min. Zeit zwischen Routenneuberechnungen
trafficLightMaxWait = 45000 -- Max. Wartezeit an Ampeln
}
}
-- Fahrer-Verhalten basierend auf Persönlichkeit einstellen
SetDriverAbility(driver, driverData.personality.caution)
SetDriverAggressiveness(driver, 1.0 - driverData.personality.caution)
-- Fahrer-Daten im Entity speichern
Entity(vehicle).state.driverData = driverData
-- Fahrer-Verhalten-Thread starten
CreateThread(function()
local lastPos = GetEntityCoords(vehicle)
local lastCheck = GetGameTimer()
while DoesEntityExist(vehicle) and DoesEntityExist(driver) do
Wait(driverData.settings.checkInterval)
local currentTime = GetGameTimer()
local timeDelta = currentTime - lastCheck
lastCheck = currentTime
-- Aktuelle Position und Geschwindigkeit
local currentPos = GetEntityCoords(vehicle)
local speed = GetEntitySpeed(vehicle)
local distanceMoved = #(lastPos - currentPos)
-- Position für Historie speichern
table.insert(driverData.state.lastPositions, 1, currentPos)
if #driverData.state.lastPositions > driverData.settings.positionHistorySize then
table.remove(driverData.state.lastPositions)
end
-- Geschwindigkeit für Historie speichern
table.insert(driverData.state.speedHistory, 1, speed)
if #driverData.state.speedHistory > 5 then
table.remove(driverData.state.speedHistory)
end
-- Durchschnittsgeschwindigkeit berechnen
local avgSpeed = 0
for _, s in ipairs(driverData.state.speedHistory) do
avgSpeed = avgSpeed + s
end
avgSpeed = avgSpeed / #driverData.state.speedHistory
-- Ampel-Erkennung
local isAtTrafficLight = IsVehicleStoppedAtTrafficLights(vehicle)
-- Ampel-Wartezustand aktualisieren
if isAtTrafficLight and not driverData.state.isWaitingAtTrafficLight then
-- Gerade an Ampel angekommen
driverData.state.isWaitingAtTrafficLight = true
driverData.state.trafficLightWaitStart = currentTime
print("^3[TAXI DEBUG]^7 Taxi waiting at traffic light")
elseif isAtTrafficLight and driverData.state.isWaitingAtTrafficLight then
-- Immer noch an Ampel
local waitTime = currentTime - driverData.state.trafficLightWaitStart
-- Wenn zu lange an Ampel, versuche weiterzufahren (Ampel könnte hängen)
if waitTime > driverData.settings.trafficLightMaxWait then
print("^3[TAXI DEBUG]^7 Taxi waited too long at traffic light, trying to continue")
-- Kurz vorwärts fahren um Ampel zu überwinden
TaskVehicleTempAction(driver, vehicle, 1, 2000) -- Forward
Wait(2000)
-- Dann normale Fahrt fortsetzen
TaxiDriverContinueRoute(driver, vehicle)
driverData.state.isWaitingAtTrafficLight = false
end
elseif not isAtTrafficLight and driverData.state.isWaitingAtTrafficLight then
-- Ampel verlassen
driverData.state.isWaitingAtTrafficLight = false
print("^2[TAXI DEBUG]^7 Taxi continued after traffic light")
end
-- Steckenbleiben-Erkennung (nicht an Ampel und kaum Bewegung)
if not isAtTrafficLight and distanceMoved < driverData.settings.stuckThreshold and speed < 0.5 then
driverData.state.stuckCounter = driverData.state.stuckCounter + 1
-- Nur alle 5 Zähler-Erhöhungen loggen
if driverData.state.stuckCounter % 5 == 0 then
print("^3[TAXI DEBUG]^7 Taxi might be stuck: " .. driverData.state.stuckCounter .. "/" .. driverData.settings.maxStuckCounter)
end
-- Wenn lange genug steckengeblieben und genug Zeit seit letztem Versuch
if driverData.state.stuckCounter >= driverData.settings.maxStuckCounter and
(currentTime - driverData.state.lastStuckRecovery) > driverData.settings.minRecoveryInterval then
print("^1[TAXI DEBUG]^7 Taxi is stuck, attempting intelligent recovery")
driverData.state.lastStuckRecovery = currentTime
driverData.state.currentBehavior = "recovery"
-- Intelligente Befreiung basierend auf Umgebung
TaxiDriverIntelligentRecovery(driver, vehicle)
-- Steckenbleiben-Zähler teilweise zurücksetzen
driverData.state.stuckCounter = math.floor(driverData.settings.maxStuckCounter * 0.4)
end
else
-- Wenn sich das Fahrzeug bewegt oder an einer Ampel steht
if isAtTrafficLight then
-- An Ampel: Zähler langsamer reduzieren
driverData.state.stuckCounter = math.max(0, driverData.state.stuckCounter - 0.2)
else
-- In Bewegung: Zähler reduzieren
driverData.state.stuckCounter = math.max(0, driverData.state.stuckCounter - 1)
-- Wenn Fahrzeug sich bewegt, aber sehr langsam über längere Zeit
if avgSpeed < 2.0 and driverData.state.stuckCounter > driverData.settings.maxStuckCounter * 0.5 and
(currentTime - driverData.state.lastRouteRecalculation) > driverData.settings.minRouteRecalcInterval then
print("^3[TAXI DEBUG]^7 Taxi moving too slow, recalculating route")
driverData.state.lastRouteRecalculation = currentTime
-- Route neu berechnen
TaxiDriverRecalculateRoute(driver, vehicle)
end
end
end
-- Verhalten basierend auf Umgebung anpassen
TaxiDriverAdaptBehavior(driver, vehicle, driverData)
lastPos = currentPos
end
end)
return driverData
end
-- Intelligente Befreiung bei Steckenbleiben
function TaxiDriverIntelligentRecovery(driver, vehicle)
if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
-- Aktuelle Position und Umgebung analysieren
local vehicleCoords = GetEntityCoords(vehicle)
local vehicleHeading = GetEntityHeading(vehicle)
local vehicleForwardVector = GetEntityForwardVector(vehicle)
-- Prüfen ob Hindernisse vorne, hinten, links, rechts
local forwardClear = not IsPositionOccupied(
vehicleCoords.x + vehicleForwardVector.x * 4.0,
vehicleCoords.y + vehicleForwardVector.y * 4.0,
vehicleCoords.z,
1.0, false, true, false, false, false, 0, false
)
local backwardClear = not IsPositionOccupied(
vehicleCoords.x - vehicleForwardVector.x * 4.0,
vehicleCoords.y - vehicleForwardVector.y * 4.0,
vehicleCoords.z,
1.0, false, true, false, false, false, 0, false
)
-- Befreiungsstrategie basierend auf Umgebung
ClearPedTasks(driver)
if backwardClear then
-- Rückwärts fahren wenn hinten frei
print("^3[TAXI DEBUG]^7 Recovery: Backing up")
TaskVehicleTempAction(driver, vehicle, 8, 2000) -- Reverse
Wait(2000)
if forwardClear then
-- Wenn vorne auch frei, einfach weiterfahren
print("^3[TAXI DEBUG]^7 Recovery: Path clear, continuing")
TaxiDriverContinueRoute(driver, vehicle)
else
-- Sonst versuchen zu wenden
print("^3[TAXI DEBUG]^7 Recovery: Turning around")
TaskVehicleTempAction(driver, vehicle, 7, 2000) -- Turn left
Wait(1000)
TaskVehicleTempAction(driver, vehicle, 8, 1000) -- Reverse
Wait(1000)
TaskVehicleTempAction(driver, vehicle, 6, 2000) -- Turn right
Wait(1000)
TaxiDriverContinueRoute(driver, vehicle)
end
elseif forwardClear then
-- Wenn nur vorne frei, vorwärts fahren
print("^3[TAXI DEBUG]^7 Recovery: Moving forward")
TaskVehicleTempAction(driver, vehicle, 1, 2000) -- Forward
Wait(2000)
TaxiDriverContinueRoute(driver, vehicle)
else
-- Wenn komplett eingeklemmt, versuche zu rütteln
print("^3[TAXI DEBUG]^7 Recovery: Trying to wiggle free")
TaskVehicleTempAction(driver, vehicle, 7, 1000) -- Turn left
Wait(1000)
TaskVehicleTempAction(driver, vehicle, 8, 800) -- Reverse
Wait(800)
TaskVehicleTempAction(driver, vehicle, 6, 1000) -- Turn right
Wait(1000)
TaskVehicleTempAction(driver, vehicle, 1, 800) -- Forward
Wait(800)
TaxiDriverContinueRoute(driver, vehicle)
end
end
-- Route neu berechnen
function TaxiDriverRecalculateRoute(driver, vehicle)
if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
-- Aktuelle Zielposition aus dem Fahrzeug-State ermitteln
local destination = Entity(vehicle).state.currentDestination
if destination then
-- Versuche einen alternativen Weg zu finden
print("^3[TAXI DEBUG]^7 Recalculating route to destination")
-- Kurz anhalten
TaskVehicleTempAction(driver, vehicle, 27, 1000) -- Stop
Wait(1000)
-- Neue Route mit leicht geändertem Fahrstil
local drivingStyle = 786603 -- Normal/Vorsichtig
TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
else
-- Wenn kein Ziel bekannt, einfach weiterfahren
TaxiDriverContinueRoute(driver, vehicle)
end
end
-- Normale Fahrt fortsetzen
function TaxiDriverContinueRoute(driver, vehicle)
if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
-- Ziel aus dem Fahrzeug-State holen
local destination = Entity(vehicle).state.currentDestination
if destination then
-- Zum Ziel fahren mit angepasster Fahrweise
local drivingStyle = 786603 -- Normal/Vorsichtig
TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
else
-- Wenn kein Ziel bekannt, einfach geradeaus fahren
TaskVehicleDriveWander(driver, vehicle, 15.0, 786603)
end
end
-- Fahrverhalten an Umgebung anpassen
function TaxiDriverAdaptBehavior(driver, vehicle, driverData)
if not DoesEntityExist(vehicle) or not DoesEntityExist(driver) then return end
-- Aktuelle Geschwindigkeit und Position
local speed = GetEntitySpeed(vehicle)
local vehicleCoords = GetEntityCoords(vehicle)
-- Verkehrsdichte in der Umgebung prüfen
local vehiclesNearby = 0
local vehicles = GetGamePool('CVehicle')
for _, otherVehicle in ipairs(vehicles) do
if otherVehicle ~= vehicle then
local otherCoords = GetEntityCoords(otherVehicle)
local distance = #(vehicleCoords - otherCoords)
if distance < 15.0 then
vehiclesNearby = vehiclesNearby + 1
end
end
end
-- Verhalten basierend auf Verkehrsdichte anpassen
local targetSpeed = driverData.personality.speedPreference
if vehiclesNearby > 5 then
-- Viel Verkehr: langsamer und vorsichtiger
targetSpeed = targetSpeed * 0.7
SetDriverAggressiveness(driver, 0.0)
elseif vehiclesNearby > 2 then
-- Moderater Verkehr: etwas langsamer
targetSpeed = targetSpeed * 0.85
SetDriverAggressiveness(driver, 0.1)
else
-- Wenig Verkehr: normale Geschwindigkeit
SetDriverAggressiveness(driver, 0.2)
end
-- Geschwindigkeit anpassen wenn nötig
if math.abs(speed - targetSpeed) > 5.0 then
if speed < targetSpeed then
-- Beschleunigen
TaskVehicleTempAction(driver, vehicle, 23, 500) -- Gentle forward
else
-- Abbremsen
TaskVehicleTempAction(driver, vehicle, 24, 500) -- Gentle brake
end
-- Nach der Anpassung normale Fahrt fortsetzen
Wait(500)
TaxiDriverContinueRoute(driver, vehicle)
end
end
-- Hilfsfunktion zur Normalisierung eines Vektors
function norm(vector)
local length = math.sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z)
if length == 0 then
return vector3(0.0, 0.0, 0.0)
end
return vector3(vector.x / length, vector.y / length, vector.z / length)
end
2025-07-30 03:21:38 +02:00
function SpawnTaxiDriver(vehicle)
print("^2[TAXI DEBUG]^7 Spawning taxi driver...")
-- Bessere Fahrer-Models mit Fallbacks
local driverModels = {
2025-07-30 09:19:32 +02:00
"A_C_Chimp", -- Taxi Driver (erste Wahl)
2025-07-30 05:48:24 +02:00
"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
2025-07-30 03:21:38 +02:00
}
local driver = nil
local driverHash = nil
-- Versuche verschiedene Models
2025-07-30 05:48:24 +02:00
for i, modelName in ipairs(driverModels) do
2025-07-30 03:21:38 +02:00
print("^2[TAXI DEBUG]^7 Trying driver model " .. i .. ": " .. modelName)
driverHash = GetHashKey(modelName)
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
-- Model laden
2025-07-30 02:17:38 +02:00
RequestModel(driverHash)
2025-07-30 03:21:38 +02:00
local timeout = GetGameTimer() + 8000 -- Längere Wartezeit
local attempts = 0
2025-07-30 02:46:15 +02:00
while not HasModelLoaded(driverHash) and GetGameTimer() < timeout do
2025-07-30 03:21:38 +02:00
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
2025-07-30 02:17:38 +02:00
Wait(100)
end
2025-07-30 02:46:15 +02:00
2025-07-30 03:21:38 +02:00
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)
2025-07-30 02:46:15 +02:00
end
2025-07-30 03:21:38 +02:00
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)
2025-07-30 05:48:24 +02:00
SetBlockingOfNonTemporaryEvents(driver, true)
2025-07-30 03:21:38 +02:00
SetPedFleeAttributes(driver, 0, 0)
SetPedCombatAttributes(driver, 17, 1)
SetPedSeeingRange(driver, 0.0)
SetPedHearingRange(driver, 0.0)
SetPedAlertness(driver, 0)
SetPedKeepTask(driver, true)
2025-07-30 09:14:44 +02:00
-- Fortgeschrittene KI-ähnliche Fahrer-Logik initialisieren
local driverData = InitializeTaxiDriverAI(driver, vehicle)
-- Zufälligen Fahrer-Namen generieren
local firstNames = {"Max", "Thomas", "Ali", "Mehmet", "Hans", "Peter", "Klaus", "Michael", "Stefan", "Frank"}
local lastNames = {"Müller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Schulz", "Hoffmann"}
local driverName = firstNames[math.random(#firstNames)] .. " " .. lastNames[math.random(#lastNames)]
-- Fahrer-Name im Fahrzeug-State speichern
Entity(vehicle).state.driverName = driverName
2025-07-30 03:21:38 +02:00
-- Fahrer-Outfit (nur wenn es ein anpassbarer Ped ist)
2025-07-30 05:48:24 +02:00
if driverHash == GetHashKey("mp_m_freemode_01") then
2025-07-30 03:21:38 +02:00
print("^2[TAXI DEBUG]^7 Setting driver outfit...")
-- Basis-Outfit für Taxi-Fahrer
2025-07-30 05:48:24 +02:00
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
2025-07-30 03:21:38 +02:00
-- 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
2025-07-30 05:48:24 +02:00
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...")
2025-07-30 09:14:44 +02:00
-- Ziel im Fahrzeug-State speichern für die KI-Logik
Entity(taxi).state.currentDestination = playerCoords
2025-07-30 05:48:24 +02:00
-- 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
2025-07-30 09:14:44 +02:00
TaskVehicleDriveToCoordLongrange(driver, taxi, nodePos.x, nodePos.y, nodePos.z, 20.0, 786603, 10.0)
2025-07-30 05:48:24 +02:00
else
print("^3[TAXI DEBUG]^7 No good vehicle node found, driving directly to player")
-- Direkt zum Spieler fahren
2025-07-30 09:14:44 +02:00
TaskVehicleDriveToCoordLongrange(driver, taxi, playerCoords.x, playerCoords.y, playerCoords.z, 20.0, 786603, 10.0)
2025-07-30 05:48:24 +02:00
end
2025-07-30 06:03:41 +02:00
end
2025-07-30 05:48:24 +02:00
2025-07-30 03:21:38 +02:00
function MonitorTaxiArrival(taxi, driver, playerCoords)
print("^2[TAXI DEBUG]^7 Monitoring taxi arrival...")
2025-07-30 05:48:24 +02:00
local arrivalTimeout = GetGameTimer() + (120 * 1000) -- 2 minute timeout
2025-07-30 03:21:38 +02:00
CreateThread(function()
while DoesEntityExist(taxi) and (not driver or DoesEntityExist(driver)) do
local taxiCoords = GetEntityCoords(taxi)
2025-07-30 05:48:24 +02:00
local currentPlayerCoords = GetEntityCoords(PlayerPedId())
local distance = #(currentPlayerCoords - taxiCoords)
2025-07-30 02:17:38 +02:00
2025-07-30 05:48:24 +02:00
-- Check if taxi is close to player
2025-07-30 05:39:51 +02:00
if distance < 15.0 then
2025-07-30 02:46:15 +02:00
print("^2[TAXI DEBUG]^7 Taxi arrived!")
2025-07-30 03:21:38 +02:00
-- Taxi stoppen
if driver and DoesEntityExist(driver) then
TaskVehicleTempAction(driver, taxi, 27, 3000) -- Brake
Wait(2000)
SetVehicleDoorsLocked(taxi, 1) -- Unlock doors
end
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Dein Taxi ist angekommen! Steige ein.',
type = 'success'
})
2025-07-30 03:21:38 +02:00
-- 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
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:21:38 +02:00
2025-07-30 05:48:24 +02:00
-- 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
2025-07-30 03:21:38 +02:00
Wait(2000)
2025-07-30 02:17:38 +02:00
end
end)
2025-07-30 05:51:20 +02:00
end
2025-07-30 03:21:38 +02:00
-- Event für Einsteigen ins Taxi
RegisterNetEvent('taxi:enterTaxi', function()
print("^2[TAXI DEBUG]^7 Player entering taxi")
2025-07-30 02:46:15 +02:00
2025-07-30 03:21:38 +02:00
if not currentTaxi or not DoesEntityExist(currentTaxi) then
print("^1[TAXI DEBUG]^7 No taxi exists")
return
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:21:38 +02:00
local playerPed = PlayerPedId()
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
-- 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
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:21:38 +02:00
TaskEnterVehicle(playerPed, currentTaxi, 10000, seatIndex, 1.0, 1, 0)
-- Warten bis eingestiegen
2025-07-30 02:17:38 +02:00
CreateThread(function()
2025-07-30 03:21:38 +02:00
local timeout = GetGameTimer() + 10000
2025-07-30 04:46:12 +02:00
local entered = false
while GetGameTimer() < timeout and not entered do
2025-07-30 02:17:38 +02:00
if IsPedInVehicle(playerPed, currentTaxi, false) then
2025-07-30 04:46:12 +02:00
entered = true
2025-07-30 03:21:38 +02:00
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()
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:21:38 +02:00
Wait(100)
2025-07-30 02:17:38 +02:00
end
2025-07-30 04:46:12 +02:00
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
2025-07-30 02:17:38 +02:00
end)
2025-07-30 03:21:38 +02:00
end)
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
function OpenDestinationMenu()
print("^2[TAXI DEBUG]^7 Opening destination menu")
2025-07-30 02:17:38 +02:00
local options = {}
-- Bekannte Ziele hinzufügen
for _, destination in pairs(Config.KnownDestinations) do
2025-07-30 03:21:38 +02:00
local distance = CalculateDistanceToCoords(destination.coords) / 1000 -- in km
local price = math.max(Config.MinFare, math.ceil(distance * Config.PricePerKm))
2025-07-30 02:17:38 +02:00
table.insert(options, {
title = destination.name,
2025-07-30 03:21:38 +02:00
description = 'Preis: $' .. price .. ' | Entfernung: ' .. math.ceil(distance * 100) / 100 .. 'km',
2025-07-30 02:17:38 +02:00
icon = 'map-marker',
onSelect = function()
2025-07-30 03:21:38 +02:00
StartTaxiRide(destination.coords, price)
2025-07-30 02:17:38 +02:00
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)
2025-07-30 03:21:38 +02:00
local distance = CalculateDistanceToCoords(coords) / 1000
local price = math.max(Config.MinFare, math.ceil(distance * Config.PricePerKm))
StartTaxiRide(coords, price)
2025-07-30 02:17:38 +02:00
else
lib.notify({
title = 'Taxi Service',
description = 'Du hast keinen Waypoint gesetzt',
type = 'error'
})
2025-07-30 03:21:38 +02:00
OpenDestinationMenu()
2025-07-30 02:17:38 +02:00
end
end
})
2025-07-30 03:21:38 +02:00
-- 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
2025-07-30 02:17:38 +02:00
-- Aussteigen Option
table.insert(options, {
title = 'Aussteigen',
description = 'Das Taxi verlassen',
icon = 'door-open',
onSelect = function()
2025-07-30 03:21:38 +02:00
ExitTaxi()
2025-07-30 02:17:38 +02:00
end
})
lib.registerContext({
2025-07-30 03:21:38 +02:00
id = 'taxi_destination_menu',
2025-07-30 02:17:38 +02:00
title = 'Taxi - Ziel wählen',
options = options
})
2025-07-30 03:21:38 +02:00
lib.showContext('taxi_destination_menu')
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:21:38 +02:00
function StartTaxiRide(destination, price)
2025-07-30 05:48:24 +02:00
print("^2[TAXI DEBUG]^7 Starting taxi ride to: " .. tostring(destination.x) .. ", " .. tostring(destination.y) .. ", " .. tostring(destination.z))
2025-07-30 03:21:38 +02:00
if not currentTaxi or not DoesEntityExist(currentTaxi) then
print("^1[TAXI DEBUG]^7 No taxi exists for ride")
return
2025-07-30 02:46:15 +02:00
end
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
-- 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'
})
2025-07-30 02:17:38 +02:00
-- Destination Blip erstellen
2025-07-30 05:48:24 +02:00
if destinationBlip then
RemoveBlip(destinationBlip)
end
2025-07-30 02:17:38 +02:00
destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z)
SetBlipSprite(destinationBlip, 1)
SetBlipColour(destinationBlip, 2)
SetBlipScale(destinationBlip, 0.8)
2025-07-30 05:48:24 +02:00
SetBlipRoute(destinationBlip, true)
2025-07-30 02:17:38 +02:00
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Taxi Ziel")
EndTextCommandSetBlipName(destinationBlip)
2025-07-30 05:48:24 +02:00
-- Taximeter starten
taxiMeter.isRunning = true
taxiMeter.startCoords = GetEntityCoords(currentTaxi)
taxiMeter.currentFare = 0
taxiMeter.pricePerKm = Config.PricePerKm
2025-07-30 09:14:44 +02:00
-- Ziel im Fahrzeug-State speichern für die KI-Logik
Entity(currentTaxi).state.currentDestination = destination
2025-07-30 05:48:24 +02:00
-- Zum Ziel fahren mit verbesserter Navigation
2025-07-30 09:14:44 +02:00
local drivingStyle = 786603 -- Normal/Vorsichtig
TaskVehicleDriveToCoordLongrange(currentDriver, currentTaxi, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
-- Fahrer-Dialog anzeigen
local driverName = Entity(currentTaxi).state.driverName or "Taxi-Fahrer"
local dialogOptions = {
"Ich bringe dich sicher ans Ziel.",
"Schönes Wetter heute, oder?",
"Ich kenne eine Abkürzung.",
"Bist du aus der Gegend?",
"Ich fahre schon seit 15 Jahren Taxi.",
"Entspann dich und genieße die Fahrt."
}
-- Zufälligen Dialog auswählen und anzeigen
Wait(3000) -- Kurz warten bevor der Fahrer spricht
lib.notify({
title = driverName,
description = dialogOptions[math.random(#dialogOptions)],
type = 'info',
icon = 'comment',
position = 'top-center',
duration = 5000
})
2025-07-30 03:21:38 +02:00
-- Fahrt überwachen
MonitorTaxiRide(destination, price)
end
function MonitorTaxiRide(destination, price)
print("^2[TAXI DEBUG]^7 Monitoring taxi ride...")
2025-07-30 02:17:38 +02:00
2025-07-30 09:14:44 +02:00
local rideTimeout = GetGameTimer() + (10 * 60 * 1000) -- 10 Minuten Timeout
2025-07-30 05:48:24 +02:00
2025-07-30 02:17:38 +02:00
CreateThread(function()
2025-07-30 03:21:38 +02:00
while DoesEntityExist(currentTaxi) and DoesEntityExist(currentDriver) do
2025-07-30 02:17:38 +02:00
local taxiCoords = GetEntityCoords(currentTaxi)
local distance = #(vector3(destination.x, destination.y, destination.z) - taxiCoords)
2025-07-30 05:48:24 +02:00
-- Überprüfen ob wir angekommen sind
2025-07-30 02:17:38 +02:00
if distance < 10.0 then
2025-07-30 03:21:38 +02:00
-- Angekommen
TaskVehicleTempAction(currentDriver, currentTaxi, 27, 3000)
2025-07-30 02:17:38 +02:00
2025-07-30 02:46:15 +02:00
print("^2[TAXI DEBUG]^7 Arrived at destination")
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
2025-07-30 03:21:38 +02:00
description = 'Du bist angekommen! Preis: $' .. price,
2025-07-30 02:17:38 +02:00
type = 'success'
})
2025-07-30 03:21:38 +02:00
-- Bezahlung
TriggerServerEvent('taxi:payFare', price)
2025-07-30 02:17:38 +02:00
2025-07-30 03:21:38 +02:00
-- Blips entfernen
if destinationBlip then
RemoveBlip(destinationBlip)
destinationBlip = nil
end
2025-07-30 09:14:44 +02:00
-- Fahrer-Dialog anzeigen
local driverName = Entity(currentTaxi).state.driverName or "Taxi-Fahrer"
local arrivalDialogs = {
"Wir sind da! Das macht dann $" .. price .. ".",
"Angekommen! $" .. price .. " bitte.",
"Hier sind wir. $" .. price .. ", bargeldlos ist auch möglich.",
"Ziel erreicht! Das macht $" .. price .. "."
}
lib.notify({
title = driverName,
description = arrivalDialogs[math.random(#arrivalDialogs)],
type = 'info',
icon = 'comment',
position = 'top-center',
duration = 5000
})
2025-07-30 03:21:38 +02:00
-- Nach 10 Sekunden Taxi despawnen
2025-07-30 02:17:38 +02:00
SetTimeout(10000, function()
2025-07-30 03:21:38 +02:00
DespawnTaxi()
2025-07-30 02:17:38 +02:00
end)
break
end
2025-07-30 09:14:44 +02:00
-- Überprüfen ob die Fahrt zu lange dauert
if GetGameTimer() > rideTimeout then
print("^1[TAXI DEBUG]^7 Taxi ride timed out!")
-- Berechne verbleibende Distanz
local taxiCoords = GetEntityCoords(currentTaxi)
local remainingDistance = #(vector3(destination.x, destination.y, destination.z) - taxiCoords)
2025-07-30 05:48:24 +02:00
2025-07-30 09:14:44 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Die Fahrt dauert zu lange. Noch ' .. math.ceil(remainingDistance) .. 'm bis zum Ziel!',
type = 'warning'
})
-- Teleportiere Taxi näher ans Ziel (75% des Weges)
local direction = vector3(
destination.x - taxiCoords.x,
destination.y - taxiCoords.y,
destination.z - taxiCoords.z
)
local normalizedDir = norm(direction)
local teleportDistance = remainingDistance * 0.75
local newPos = vector3(
taxiCoords.x + normalizedDir.x * teleportDistance,
taxiCoords.y + normalizedDir.y * teleportDistance,
taxiCoords.z
)
-- Finde gültige Z-Koordinate
local success, groundZ = GetGroundZFor_3dCoord(newPos.x, newPos.y, newPos.z, true)
if success then
newPos = vector3(newPos.x, newPos.y, groundZ)
2025-07-30 05:48:24 +02:00
end
2025-07-30 09:14:44 +02:00
-- Teleportiere Taxi
SetEntityCoords(currentTaxi, newPos.x, newPos.y, newPos.z, false, false, false, false)
-- Neues Timeout setzen (2 Minuten)
rideTimeout = GetGameTimer() + (2 * 60 * 1000)
2025-07-30 05:48:24 +02:00
end
2025-07-30 02:17:38 +02:00
Wait(2000)
end
end)
2025-07-30 06:03:41 +02:00
end
2025-07-30 03:21:38 +02:00
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'
})
2025-07-30 06:03:41 +02:00
end
2025-07-30 03:21:38 +02:00
function ExitTaxi()
2025-07-30 02:46:15 +02:00
print("^2[TAXI DEBUG]^7 Player exiting taxi")
2025-07-30 03:21:38 +02:00
if not currentTaxi or not DoesEntityExist(currentTaxi) then
return
end
2025-07-30 02:17:38 +02:00
local playerPed = PlayerPedId()
TaskLeaveVehicle(playerPed, currentTaxi, 0)
lib.notify({
title = 'Taxi Service',
description = 'Du bist ausgestiegen',
type = 'info'
})
2025-07-30 03:21:38 +02:00
-- Taxi nach 5 Sekunden despawnen
2025-07-30 02:17:38 +02:00
SetTimeout(5000, function()
2025-07-30 03:21:38 +02:00
DespawnTaxi()
2025-07-30 02:17:38 +02:00
end)
2025-07-30 06:03:41 +02:00
end
2025-07-30 03:21:38 +02:00
function DespawnTaxi()
print("^2[TAXI DEBUG]^7 Despawning taxi")
2025-07-30 02:17:38 +02:00
2025-07-30 03:50:38 +02:00
if not currentTaxi or not DoesEntityExist(currentTaxi) then
return
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:50:38 +02:00
-- 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
2025-07-30 05:48:24 +02:00
TaskVehicleDriveToCoordLongrange(currentDriver, currentTaxi, driveToX, driveToY, taxiCoords.z, 25.0, 786603, 5.0)
2025-07-30 03:50:38 +02:00
-- Blips entfernen
if taxiBlip then
RemoveBlip(taxiBlip)
taxiBlip = nil
end
2025-07-30 04:08:22 +02:00
if mapBlip then
RemoveBlip(mapBlip)
mapBlip = nil
end
2025-07-30 03:50:38 +02:00
if destinationBlip then
RemoveBlip(destinationBlip)
destinationBlip = nil
2025-07-30 03:54:22 +02:00
end
2025-07-30 03:50:38 +02:00
-- 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
2025-07-30 04:08:22 +02:00
if mapBlip then
RemoveBlip(mapBlip)
mapBlip = nil
end
2025-07-30 03:50:38 +02:00
if destinationBlip then
RemoveBlip(destinationBlip)
destinationBlip = nil
2025-07-30 03:54:22 +02:00
end
2025-07-30 03:50:38 +02:00
-- 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)")
2025-07-30 02:17:38 +02:00
end
2025-07-30 06:03:41 +02:00
end
2025-07-30 03:54:22 +02:00
2025-07-30 03:21:38 +02:00
function CalculateDistanceToCoords(coords)
local playerCoords = GetEntityCoords(PlayerPedId())
return #(playerCoords - coords)
2025-07-30 06:03:41 +02:00
end
2025-07-30 03:21:38 +02:00
-- 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'
})
2025-07-30 02:17:38 +02:00
end
end)
2025-07-30 04:47:41 +02:00
-- Thread zum Überwachen der Tasten im Taxi
2025-07-30 04:46:12 +02:00
CreateThread(function()
while true do
Wait(0)
if currentTaxi and DoesEntityExist(currentTaxi) then
local playerPed = PlayerPedId()
if IsPedInVehicle(playerPed, currentTaxi, false) then
2025-07-30 04:47:41 +02:00
-- Zeige Hinweise an
local helpText = '[E] - Ziel wählen [F] - Fahrt beenden'
lib.showTextUI(helpText, {
2025-07-30 04:46:12 +02:00
position = "top-center",
2025-07-30 04:47:41 +02:00
icon = 'taxi',
2025-07-30 04:46:12 +02:00
style = {
borderRadius = 10,
backgroundColor = '#48BB78',
color = 'white'
}
})
-- Wenn E gedrückt wird, öffne Menü
if IsControlJustReleased(0, 38) then -- E Taste
OpenDestinationMenu()
end
2025-07-30 04:47:41 +02:00
-- Wenn F gedrückt wird, beende Fahrt
if IsControlJustReleased(0, 23) then -- F Taste
lib.hideTextUI()
EndTaxiRide()
end
2025-07-30 04:46:12 +02:00
else
lib.hideTextUI()
end
else
lib.hideTextUI()
Wait(1000) -- Längere Wartezeit wenn kein Taxi existiert
end
end
end)
2025-07-30 04:47:41 +02:00
-- 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)
2025-07-30 06:03:41 +02:00
end
2025-07-30 04:46:12 +02:00
-- 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'
}
})
2025-07-30 05:48:24 +02:00
-- Prüfen ob E gedrückt wird
2025-07-30 04:46:12 +02:00
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
2025-07-30 05:48:24 +02:00
CreateThread(function()
while GetGameTimer() < entryTimeout do
if IsPedInVehicle(playerPed, currentTaxi, false) then
-- Spieler ist eingestiegen
Wait(1000)
OpenDestinationMenu()
break
end
Wait(100)
2025-07-30 04:46:12 +02:00
end
2025-07-30 05:48:24 +02:00
end)
2025-07-30 04:46:12 +02:00
end
else
lib.hideTextUI()
end
end
end
end)
2025-07-30 03:21:38 +02:00
-- Cleanup beim Resource Stop
2025-07-30 02:17:38 +02:00
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
2025-07-30 03:21:38 +02:00
print("^2[TAXI DEBUG]^7 Cleaning up main script...")
2025-07-30 05:48:24 +02:00
-- UI ausblenden
lib.hideTextUI()
-- Taxi despawnen
2025-07-30 03:21:38 +02:00
DespawnTaxi()
2025-07-30 05:48:24 +02:00
2025-07-30 03:21:38 +02:00
print("^2[TAXI DEBUG]^7 Main cleanup completed")
2025-07-30 02:17:38 +02:00
end
end)
2025-07-30 09:14:44 +02:00