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

1595 lines
61 KiB
Lua
Raw Normal View History

2025-07-30 04:57:57 +02:00
local QBCore = exports['qb-core']:GetCoreObject()
2025-07-30 02:17:38 +02:00
local stationVehicles = {}
local stationBlips = {}
2025-07-31 07:41:08 +02:00
function DebugPrint(type, message)
if Config.Debug then
if type == "error" then
print("^1[TAXI STATIONS DEBUG]^7 " .. message)
elseif type == "warning" then
print("^3[TAXI STATIONS DEBUG]^7 " .. message)
else -- success/info
print("^2[TAXI STATIONS DEBUG]^7 " .. message)
end
end
end
local QBCore = exports['qb-core']:GetCoreObject()
local stationVehicles = {}
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Stations script loaded")
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
-- Taxi Stationen initialisieren
CreateThread(function()
2025-07-30 07:32:37 +02:00
Wait(5000) -- Längere Wartezeit, um sicherzustellen, dass alle Ressourcen geladen sind
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Initializing taxi stations...")
2025-07-30 02:17:38 +02:00
InitializeTaxiStations()
2025-07-30 07:32:37 +02:00
-- Regelmäßige Überprüfung und Wiederherstellung der Stationen
CreateThread(function()
while true do
Wait(60000) -- Alle 60 Sekunden prüfen
CheckAndRestoreStationVehicles()
end
end)
2025-07-30 02:17:38 +02:00
end)
function InitializeTaxiStations()
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 InitializeTaxiStations started")
2025-07-30 02:46:15 +02:00
if not Config.TaxiStations then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Config.TaxiStations not found!")
2025-07-30 02:46:15 +02:00
return
end
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Found " .. #Config.TaxiStations .. " stations")
2025-07-30 02:46:15 +02:00
2025-07-30 07:32:37 +02:00
-- Zuerst alle bestehenden Fahrzeuge und Blips entfernen
CleanupExistingStations()
-- Dann neue erstellen
2025-07-30 02:17:38 +02:00
for stationId, station in pairs(Config.TaxiStations) do
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Processing station " .. stationId .. ": " .. station.name)
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
-- Blip für Station erstellen
local blip = AddBlipForCoord(station.blipCoords.x, station.blipCoords.y, station.blipCoords.z)
SetBlipSprite(blip, 198)
SetBlipColour(blip, 5)
SetBlipScale(blip, 0.8)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(station.name)
EndTextCommandSetBlipName(blip)
stationBlips[stationId] = blip
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Blip created for station " .. stationId)
2025-07-30 02:17:38 +02:00
-- Fahrzeuge an Station spawnen
stationVehicles[stationId] = {}
2025-07-30 07:32:37 +02:00
-- Verzögertes Spawnen der Fahrzeuge, um Ressourcenkonflikte zu vermeiden
CreateThread(function()
for vehicleId, vehicleData in pairs(station.vehicles) do
-- Kleine Verzögerung zwischen jedem Fahrzeug
Wait(500)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Spawning vehicle " .. vehicleId .. " (" .. vehicleData.model .. ") at station " .. stationId)
2025-07-30 07:32:37 +02:00
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end
end)
end
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 All stations initialization started")
2025-07-30 07:32:37 +02:00
end
function CleanupExistingStations()
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Cleaning up existing stations...")
2025-07-30 07:32:37 +02:00
-- Alle bestehenden Fahrzeuge löschen
for stationId, vehicles in pairs(stationVehicles) do
for vehicleId, vehicleInfo in pairs(vehicles) do
if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then
exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity)
DeleteEntity(vehicleInfo.entity)
end
if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then
DeleteEntity(vehicleInfo.driver)
end
2025-07-30 02:17:38 +02:00
end
end
2025-07-30 02:46:15 +02:00
2025-07-30 07:32:37 +02:00
-- Alle Blips entfernen
for _, blip in pairs(stationBlips) do
RemoveBlip(blip)
end
-- Variablen zurücksetzen
stationVehicles = {}
stationBlips = {}
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Cleanup completed")
2025-07-30 02:17:38 +02:00
end
function SpawnStationVehicle(stationId, vehicleId, vehicleData)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 SpawnStationVehicle: " .. stationId .. "/" .. vehicleId)
2025-07-30 02:46:15 +02:00
2025-07-30 07:32:37 +02:00
-- Prüfen ob bereits ein Fahrzeug für diese Position existiert
if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] and
stationVehicles[stationId][vehicleId].entity and
DoesEntityExist(stationVehicles[stationId][vehicleId].entity) then
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Vehicle already exists for this position, removing it first")
2025-07-30 07:32:37 +02:00
exports['qb-target']:RemoveTargetEntity(stationVehicles[stationId][vehicleId].entity)
DeleteEntity(stationVehicles[stationId][vehicleId].entity)
end
-- Prüfen ob die Position frei ist
local clearArea = true
local vehicles = GetGamePool('CVehicle')
for _, vehicle in ipairs(vehicles) do
local vehCoords = GetEntityCoords(vehicle)
if #(vector3(vehicleData.coords.x, vehicleData.coords.y, vehicleData.coords.z) - vehCoords) < 3.0 then
clearArea = false
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Position blocked by another vehicle, will retry later")
2025-07-30 07:32:37 +02:00
-- Nach 30 Sekunden erneut versuchen
SetTimeout(30000, function()
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end)
return
end
end
2025-07-30 02:17:38 +02:00
CreateThread(function()
local vehicleHash = GetHashKey(vehicleData.model)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Vehicle hash: " .. vehicleHash)
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
RequestModel(vehicleHash)
2025-07-30 02:46:15 +02:00
local timeout = GetGameTimer() + 10000
while not HasModelLoaded(vehicleHash) and GetGameTimer() < timeout do
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Waiting for model " .. vehicleData.model .. " to load...")
2025-07-30 02:17:38 +02:00
Wait(100)
end
2025-07-30 02:46:15 +02:00
if not HasModelLoaded(vehicleHash) then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Failed to load model: " .. vehicleData.model)
2025-07-30 07:32:37 +02:00
-- Nach 30 Sekunden erneut versuchen
SetTimeout(30000, function()
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end)
2025-07-30 02:46:15 +02:00
return
end
2025-07-30 02:17:38 +02:00
local vehicle = CreateVehicle(
vehicleHash,
vehicleData.coords.x,
vehicleData.coords.y,
vehicleData.coords.z,
vehicleData.coords.w,
false,
false
)
2025-07-30 02:46:15 +02:00
if not DoesEntityExist(vehicle) then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Failed to create vehicle!")
2025-07-30 07:32:37 +02:00
-- Nach 30 Sekunden erneut versuchen
SetTimeout(30000, function()
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end)
2025-07-30 02:46:15 +02:00
return
end
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Vehicle created: " .. vehicle)
2025-07-30 02:46:15 +02:00
2025-07-30 07:32:37 +02:00
-- Fahrzeug konfigurieren
2025-07-30 02:17:38 +02:00
SetEntityAsMissionEntity(vehicle, true, true)
SetVehicleOnGroundProperly(vehicle)
SetVehicleDoorsLocked(vehicle, 2) -- Locked
SetVehicleEngineOn(vehicle, false, true, false)
2025-07-30 07:32:37 +02:00
-- Verbesserte Persistenz
SetEntityInvincible(vehicle, true)
SetVehicleCanBeVisiblyDamaged(vehicle, false)
SetEntityProofs(vehicle, true, true, true, true, true, true, true, true)
SetVehicleExplodesOnHighExplosionDamage(vehicle, false)
SetVehicleHasBeenOwnedByPlayer(vehicle, true)
SetVehicleIsConsideredByPlayer(vehicle, true)
2025-07-30 02:17:38 +02:00
-- Fahrzeug-Info speichern
2025-07-30 07:32:37 +02:00
if not stationVehicles[stationId] then
stationVehicles[stationId] = {}
end
2025-07-30 02:17:38 +02:00
stationVehicles[stationId][vehicleId] = {
entity = vehicle,
data = vehicleData,
occupied = false
}
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Adding qb-target for vehicle " .. vehicle)
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
-- qb-target für Fahrzeug hinzufügen
exports['qb-target']:AddTargetEntity(vehicle, {
options = {
{
type = "client",
event = "taxi:enterStationVehicle",
icon = "fas fa-taxi",
label = "Taxi nehmen ($" .. vehicleData.pricePerKm .. "/km)",
stationId = stationId,
vehicleId = vehicleId
}
},
distance = 3.0
})
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 qb-target added for vehicle " .. vehicle)
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
SetModelAsNoLongerNeeded(vehicleHash)
end)
end
2025-07-30 07:32:37 +02:00
function CheckAndRestoreStationVehicles()
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Checking station vehicles...")
2025-07-30 07:32:37 +02:00
local restoredCount = 0
for stationId, station in pairs(Config.TaxiStations) do
if not stationVehicles[stationId] then
stationVehicles[stationId] = {}
end
for vehicleId, vehicleData in pairs(station.vehicles) do
local shouldSpawn = false
-- Prüfen ob das Fahrzeug existiert
if not stationVehicles[stationId][vehicleId] then
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Vehicle data missing for station " .. stationId .. ", vehicle " .. vehicleId)
2025-07-30 07:32:37 +02:00
shouldSpawn = true
elseif not stationVehicles[stationId][vehicleId].entity then
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Vehicle entity missing for station " .. stationId .. ", vehicle " .. vehicleId)
2025-07-30 07:32:37 +02:00
shouldSpawn = true
elseif not DoesEntityExist(stationVehicles[stationId][vehicleId].entity) then
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Vehicle entity doesn't exist for station " .. stationId .. ", vehicle " .. vehicleId)
2025-07-30 07:32:37 +02:00
shouldSpawn = true
elseif stationVehicles[stationId][vehicleId].occupied then
-- Fahrzeug ist besetzt, nicht neu spawnen
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Vehicle at station " .. stationId .. ", vehicle " .. vehicleId .. " is occupied")
2025-07-30 07:32:37 +02:00
shouldSpawn = false
end
if shouldSpawn then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station " .. stationId .. ", vehicle " .. vehicleId)
2025-07-30 07:32:37 +02:00
SpawnStationVehicle(stationId, vehicleId, vehicleData)
restoredCount = restoredCount + 1
-- Kleine Verzögerung zwischen Spawns
Wait(500)
end
end
end
if restoredCount > 0 then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Restored " .. restoredCount .. " station vehicles")
2025-07-30 07:32:37 +02:00
else
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 All station vehicles are present")
2025-07-30 07:32:37 +02:00
end
end
2025-07-30 08:49:39 +02:00
-- Fortgeschrittenes Taxi-Fahrer-Verhaltenssystem
function InitializeTaxiDriverAI(driver, vehicle)
if not driver or not DoesEntityExist(driver) then return end
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Initializing advanced taxi driver AI")
2025-07-30 08:49:39 +02:00
-- 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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Taxi waiting at traffic light")
2025-07-30 08:49:39 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Taxi waited too long at traffic light, trying to continue")
2025-07-30 08:49:39 +02:00
-- 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
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Taxi continued after traffic light")
2025-07-30 08:49:39 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Taxi might be stuck: " .. driverData.state.stuckCounter .. "/" .. driverData.settings.maxStuckCounter)
2025-07-30 08:49:39 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Taxi is stuck, attempting intelligent recovery")
2025-07-30 08:49:39 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Taxi moving too slow, recalculating route")
2025-07-30 08:49:39 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Recovery: Backing up")
2025-07-30 08:49:39 +02:00
TaskVehicleTempAction(driver, vehicle, 8, 2000) -- Reverse
Wait(2000)
if forwardClear then
-- Wenn vorne auch frei, einfach weiterfahren
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Recovery: Path clear, continuing")
2025-07-30 08:49:39 +02:00
TaxiDriverContinueRoute(driver, vehicle)
else
-- Sonst versuchen zu wenden
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Recovery: Turning around")
2025-07-30 08:49:39 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Recovery: Moving forward")
2025-07-30 08:49:39 +02:00
TaskVehicleTempAction(driver, vehicle, 1, 2000) -- Forward
Wait(2000)
TaxiDriverContinueRoute(driver, vehicle)
else
-- Wenn komplett eingeklemmt, versuche zu rütteln
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Recovery: Trying to wiggle free")
2025-07-30 08:49:39 +02:00
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
-- Da wir die nicht direkt auslesen können, nehmen wir an, dass es das aktuelle Ziel ist
local destination = Entity(vehicle).state.currentDestination
if destination then
-- Versuche einen alternativen Weg zu finden
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Recalculating route to destination")
2025-07-30 08:49:39 +02:00
-- 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
2025-07-30 03:05:27 +02:00
-- Hilfsfunktion um Spieler-Sitz zu ermitteln
function GetPlayerVehicleSeat(ped, vehicle)
if not IsPedInVehicle(ped, vehicle, false) then
return nil
end
-- Alle möglichen Sitze prüfen
for seat = -1, 7 do -- -1 = Fahrer, 0 = Beifahrer, 1+ = Hintersitze
if GetPedInVehicleSeat(vehicle, seat) == ped then
return seat
end
end
return nil
end
2025-07-30 02:17:38 +02:00
-- Event für Einsteigen in Station-Taxi
RegisterNetEvent('taxi:enterStationVehicle', function(data)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Player trying to enter station vehicle")
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Data: " .. json.encode(data))
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
local stationId = data.stationId
local vehicleId = data.vehicleId
if not stationVehicles[stationId] or not stationVehicles[stationId][vehicleId] then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Vehicle not found in data")
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Dieses Taxi ist nicht verfügbar',
type = 'error'
})
return
end
local vehicleInfo = stationVehicles[stationId][vehicleId]
if vehicleInfo.occupied then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Vehicle already occupied")
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Dieses Taxi ist bereits besetzt',
type = 'error'
})
return
end
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Entering vehicle...")
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
-- Spieler ins Fahrzeug setzen
local playerPed = PlayerPedId()
local vehicle = vehicleInfo.entity
-- Türen entsperren
SetVehicleDoorsLocked(vehicle, 1)
2025-07-30 02:59:59 +02:00
-- Info-Text anzeigen während Fahrer geladen wird
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Showing driver loading text...")
2025-07-30 03:12:57 +02:00
lib.showTextUI('🚕 Warte an der Station - Fahrer wird geladen...', {
2025-07-30 02:59:59 +02:00
position = "top-center",
icon = 'taxi',
style = {
borderRadius = 10,
backgroundColor = '#48BB78',
color = 'white'
}
})
2025-07-30 03:12:57 +02:00
-- Kurz warten damit der Text sichtbar wird
Wait(1000)
2025-07-30 02:53:21 +02:00
-- Verschiedene Fahrer-Models versuchen
local driverModels = {
2025-07-30 09:20:07 +02:00
"A_C_Chimp", -- Taxi Driver (erste Wahl)
2025-07-30 07:32:37 +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 02:53:21 +02:00
}
local driver = nil
local driverHash = nil
2025-07-30 03:21:38 +02:00
for i, modelName in pairs(driverModels) do
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Trying driver model " .. i .. ": " .. modelName)
2025-07-30 02:53:21 +02:00
driverHash = GetHashKey(modelName)
2025-07-30 03:12:57 +02:00
-- Text während Model-Loading aktualisieren
2025-07-30 03:21:38 +02:00
lib.showTextUI('🚕 Lade Fahrer-Model (' .. i .. '/' .. #driverModels .. '): ' .. modelName .. '...', {
2025-07-30 03:12:57 +02:00
position = "top-center",
icon = 'taxi',
style = {
borderRadius = 10,
backgroundColor = '#48BB78',
color = 'white'
}
})
2025-07-30 02:53:21 +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:53:21 +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
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Still waiting for model " .. modelName .. " (attempt " .. attempts .. ")")
2025-07-30 03:21:38 +02:00
end
2025-07-30 02:53:21 +02:00
Wait(100)
end
if HasModelLoaded(driverHash) then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Driver model " .. modelName .. " loaded successfully")
2025-07-30 02:53:21 +02:00
2025-07-30 03:12:57 +02:00
-- Text aktualisieren
lib.showTextUI('🚕 Erstelle Fahrer...', {
position = "top-center",
icon = 'taxi',
style = {
borderRadius = 10,
backgroundColor = '#48BB78',
color = 'white'
}
})
2025-07-30 02:53:21 +02:00
driver = CreatePedInsideVehicle(vehicle, 26, driverHash, -1, true, false)
if DoesEntityExist(driver) then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Driver created successfully: " .. driver)
2025-07-30 02:53:21 +02:00
break
else
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Failed to create driver with model: " .. modelName)
2025-07-30 02:53:21 +02:00
SetModelAsNoLongerNeeded(driverHash)
end
else
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Failed to load driver model: " .. modelName)
2025-07-30 03:21:38 +02:00
SetModelAsNoLongerNeeded(driverHash)
2025-07-30 02:53:21 +02:00
end
2025-07-30 03:12:57 +02:00
Wait(500) -- Kurze Pause zwischen Versuchen
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
-- Fallback: Notfall-Fahrer erstellen
2025-07-30 02:53:21 +02:00
if not driver or not DoesEntityExist(driver) then
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Using emergency fallback driver creation...")
2025-07-30 02:53:21 +02:00
2025-07-30 03:21:38 +02:00
lib.showTextUI('🚕 Erstelle Notfall-Fahrer...', {
2025-07-30 03:12:57 +02:00
position = "top-center",
icon = 'taxi',
style = {
borderRadius = 10,
backgroundColor = '#FFA500',
color = 'white'
}
})
2025-07-30 03:21:38 +02:00
-- 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`
}
2025-07-30 02:53:21 +02:00
2025-07-30 03:21:38 +02:00
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
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Emergency driver created")
2025-07-30 03:21:38 +02:00
driverHash = hash
break
end
SetModelAsNoLongerNeeded(hash)
end
2025-07-30 02:53:21 +02:00
end
2025-07-30 03:12:57 +02:00
2025-07-30 03:21:38 +02:00
Wait(1000)
2025-07-30 02:46:15 +02:00
end
2025-07-30 02:53:21 +02:00
-- Wenn immer noch kein Fahrer, ohne Fahrer fortfahren
if not driver or not DoesEntityExist(driver) then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Could not create any driver, continuing without driver")
2025-07-30 03:12:57 +02:00
lib.showTextUI('❌ Kein Fahrer verfügbar - Du kannst selbst fahren', {
position = "top-center",
icon = 'exclamation-triangle',
style = {
borderRadius = 10,
backgroundColor = '#FF6B6B',
color = 'white'
}
})
Wait(3000) -- 3 Sekunden anzeigen
lib.hideTextUI()
2025-07-30 02:53:21 +02:00
driver = nil
2025-07-30 02:59:59 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Kein Fahrer verfügbar - Du kannst das Taxi selbst fahren',
type = 'warning'
})
2025-07-30 02:53:21 +02:00
else
2025-07-30 03:12:57 +02:00
-- Fahrer erfolgreich erstellt
lib.showTextUI('✅ Fahrer bereit - Steige hinten ein!', {
position = "top-center",
icon = 'check-circle',
style = {
borderRadius = 10,
backgroundColor = '#48BB78',
color = 'white'
}
})
Wait(2000) -- 2 Sekunden anzeigen
2025-07-30 02:53:21 +02:00
-- Fahrer konfigurieren
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)
2025-07-30 03:21:38 +02:00
SetBlockingOfNonTemporaryEvents(driver, true)
2025-07-30 02:53:21 +02:00
2025-07-30 08:49:39 +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 02:59:59 +02:00
lib.notify({
title = 'Taxi Service',
2025-07-30 08:49:39 +02:00
description = 'Dein Fahrer ' .. driverName .. ' ist bereit - Steige hinten ein',
2025-07-30 02:59:59 +02:00
type = 'success'
})
end
-- Spieler HINTEN einsteigen lassen
2025-07-30 03:05:27 +02:00
local seatIndex = 1 -- Hinten links als Standard
2025-07-30 02:59:59 +02:00
-- Prüfen welche Hintersitze verfügbar sind
local availableSeats = {}
for i = 1, 3 do -- Sitze 1, 2, 3 (hinten links, hinten mitte, hinten rechts)
2025-07-30 03:05:27 +02:00
if IsVehicleSeatFree(vehicle, i) then
2025-07-30 02:59:59 +02:00
table.insert(availableSeats, i)
end
2025-07-30 02:46:15 +02:00
end
2025-07-30 02:59:59 +02:00
-- Ersten verfügbaren Hintersitz wählen
if #availableSeats > 0 then
seatIndex = availableSeats[1]
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Using rear seat: " .. seatIndex)
2025-07-30 02:59:59 +02:00
else
-- Fallback: Beifahrersitz
seatIndex = 0
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 No rear seats available, using passenger seat")
2025-07-30 02:59:59 +02:00
end
-- Spieler in den gewählten Sitz einsteigen lassen
TaskEnterVehicle(playerPed, vehicle, 10000, seatIndex, 1.0, 1, 0)
-- Info-Text während Einsteigen
lib.showTextUI('🚕 Steige ins Taxi ein...', {
position = "top-center",
icon = 'car-side',
style = {
borderRadius = 10,
backgroundColor = '#4299E1',
color = 'white'
}
})
2025-07-30 02:17:38 +02:00
-- Warten bis Spieler eingestiegen ist
CreateThread(function()
2025-07-30 02:59:59 +02:00
local enterTimeout = GetGameTimer() + 15000 -- Längere Zeit für Einsteigen
local hasEntered = false
while GetGameTimer() < enterTimeout and not hasEntered do
2025-07-30 02:17:38 +02:00
if IsPedInVehicle(playerPed, vehicle, false) then
2025-07-30 02:59:59 +02:00
hasEntered = true
2025-07-30 02:17:38 +02:00
vehicleInfo.occupied = true
vehicleInfo.driver = driver
2025-07-30 02:59:59 +02:00
-- Info-Text verstecken
lib.hideTextUI()
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Player entered successfully")
2025-07-30 02:59:59 +02:00
-- Prüfen ob Spieler wirklich hinten sitzt
2025-07-30 03:05:27 +02:00
local playerSeat = GetPlayerVehicleSeat(playerPed, vehicle)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Player is in seat: " .. tostring(playerSeat))
2025-07-30 03:05:27 +02:00
2025-07-30 02:59:59 +02:00
if playerSeat == -1 then -- Fahrersitz
2025-07-31 07:35:56 +02:00
DebugPrint("^3[TAXI STATIONS DEBUG]^7 Player is in driver seat, moving to passenger area")
2025-07-30 02:59:59 +02:00
if driver and DoesEntityExist(driver) then
2025-07-30 03:05:27 +02:00
-- Spieler zum nächsten verfügbaren Sitz bewegen
Wait(1000)
2025-07-30 02:59:59 +02:00
TaskShuffleToNextVehicleSeat(playerPed, vehicle)
Wait(2000)
end
end
lib.notify({
title = 'Taxi Service',
description = 'Willkommen im Taxi! Wähle dein Ziel.',
type = 'success'
})
2025-07-30 02:17:38 +02:00
-- Ziel-Menu öffnen
2025-07-30 02:59:59 +02:00
Wait(1000) -- Kurz warten damit Einsteigen abgeschlossen ist
2025-07-30 02:17:38 +02:00
OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, vehicleInfo.data.pricePerKm)
break
end
Wait(100)
end
2025-07-30 02:46:15 +02:00
2025-07-30 02:59:59 +02:00
if not hasEntered then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Player failed to enter vehicle")
2025-07-30 02:59:59 +02:00
-- Info-Text verstecken
lib.hideTextUI()
lib.notify({
title = 'Taxi Service',
description = 'Einsteigen fehlgeschlagen',
type = 'error'
})
2025-07-30 02:46:15 +02:00
-- Cleanup
2025-07-30 02:53:21 +02:00
if driver and DoesEntityExist(driver) then
2025-07-30 02:46:15 +02:00
DeleteEntity(driver)
end
SetVehicleDoorsLocked(vehicle, 2)
2025-07-30 02:59:59 +02:00
vehicleInfo.occupied = false
2025-07-30 02:46:15 +02:00
end
2025-07-30 02:17:38 +02:00
end)
end)
function OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Opening station taxi menu")
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
local options = {}
-- Bekannte Ziele hinzufügen
for _, destination in pairs(Config.KnownDestinations) do
local customPrice = math.max(Config.MinFare, math.ceil((CalculateDistanceToCoords(destination.coords) / 1000) * pricePerKm))
table.insert(options, {
title = destination.name,
2025-07-30 02:46:15 +02:00
description = 'Preis: $' .. customPrice .. ' | Entfernung: ' .. math.ceil(CalculateDistanceToCoords(destination.coords) / 1000 * 100) / 100 .. 'km',
2025-07-30 02:17:38 +02:00
icon = 'map-marker',
onSelect = function()
StartStationTaxiRide(stationId, vehicleId, vehicle, driver, destination.coords, customPrice)
end
})
end
2025-07-30 02:46:15 +02:00
-- Andere Taxi-Stationen als Ziele
table.insert(options, {
title = '📍 Andere Taxi-Stationen',
description = 'Fahre zu einer anderen Taxi-Station',
icon = 'taxi',
onSelect = function()
OpenStationSelectionMenu(stationId, vehicleId, vehicle, driver, pricePerKm)
end
})
2025-07-30 02:17:38 +02:00
-- Waypoint Option
table.insert(options, {
title = 'Zu meinem Waypoint',
description = 'Fahre zu deinem gesetzten Waypoint',
icon = 'location-dot',
onSelect = function()
local waypoint = GetFirstBlipInfoId(8)
if DoesBlipExist(waypoint) then
local coords = GetBlipInfoIdCoord(waypoint)
local distance = CalculateDistanceToCoords(coords) / 1000
local price = math.max(Config.MinFare, math.ceil(distance * pricePerKm))
StartStationTaxiRide(stationId, vehicleId, vehicle, driver, coords, price)
else
lib.notify({
title = 'Taxi Service',
description = 'Du hast keinen Waypoint gesetzt',
type = 'error'
})
OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm)
end
end
})
2025-07-30 02:53:21 +02:00
-- Selbst fahren Option (wenn kein Fahrer)
if not driver or not DoesEntityExist(driver) then
table.insert(options, {
title = '🚗 Selbst fahren',
description = 'Du fährst das Taxi selbst (kostenlos)',
icon = 'car',
onSelect = function()
SelfDriveStationTaxi(stationId, vehicleId, vehicle)
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()
ExitStationTaxi(stationId, vehicleId, vehicle, driver)
end
})
lib.registerContext({
id = 'station_taxi_menu',
2025-07-30 02:46:15 +02:00
title = 'Taxi - Ziel wählen (' .. Config.TaxiStations[stationId].name .. ')',
2025-07-30 02:17:38 +02:00
options = options
})
lib.showContext('station_taxi_menu')
end
2025-07-30 02:53:21 +02:00
function SelfDriveStationTaxi(stationId, vehicleId, vehicle)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Player driving taxi themselves")
2025-07-30 02:53:21 +02:00
local playerPed = PlayerPedId()
-- Spieler zum Fahrersitz bewegen
TaskShuffleToNextVehicleSeat(playerPed, vehicle)
lib.notify({
title = 'Taxi Service',
description = 'Du fährst das Taxi jetzt selbst. Bringe es zur Station zurück wenn du fertig bist.',
type = 'info'
})
-- Überwachung für Rückgabe
CreateThread(function()
while DoesEntityExist(vehicle) do
local playerPed = PlayerPedId()
-- Prüfen ob Spieler noch im Fahrzeug ist
if not IsPedInVehicle(playerPed, vehicle, false) then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Player left self-drive taxi")
2025-07-30 02:53:21 +02:00
-- Nach 30 Sekunden Taxi zurück zur Station
SetTimeout(30000, function()
ReturnTaxiToStation(stationId, vehicleId, vehicle, nil)
end)
break
end
Wait(5000)
end
end)
end
2025-07-30 02:46:15 +02:00
function OpenStationSelectionMenu(stationId, vehicleId, vehicle, driver, pricePerKm)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Opening station selection menu")
2025-07-30 02:46:15 +02:00
local options = {}
for otherStationId, station in pairs(Config.TaxiStations) do
if otherStationId ~= stationId then
local distance = CalculateDistanceToCoords(station.blipCoords) / 1000
local price = math.max(Config.MinFare, math.ceil(distance * pricePerKm))
table.insert(options, {
title = station.name,
description = 'Preis: $' .. price .. ' | Entfernung: ' .. math.ceil(distance * 100) / 100 .. 'km',
icon = 'building',
onSelect = function()
StartStationTaxiRide(stationId, vehicleId, vehicle, driver, station.blipCoords, price)
end
})
end
end
-- Zurück Option
table.insert(options, {
title = '← Zurück',
description = 'Zurück zum Hauptmenü',
icon = 'arrow-left',
onSelect = function()
OpenStationTaxiMenu(stationId, vehicleId, vehicle, driver, pricePerKm)
end
})
lib.registerContext({
id = 'station_selection_menu',
title = 'Taxi-Stationen',
options = options
})
lib.showContext('station_selection_menu')
end
2025-07-30 02:17:38 +02:00
function StartStationTaxiRide(stationId, vehicleId, vehicle, driver, destination, price)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Starting station taxi ride to: " .. tostring(destination.x) .. ", " .. tostring(destination.y) .. ", " .. tostring(destination.z))
2025-07-30 02:46:15 +02:00
2025-07-30 02:53:21 +02:00
-- Wenn kein Fahrer, Spieler selbst fahren lassen
if not driver or not DoesEntityExist(driver) then
lib.notify({
title = 'Taxi Service',
description = 'Kein Fahrer verfügbar. Fahre selbst zum Ziel! (Kostenlos)',
type = 'info'
})
-- Destination Blip erstellen
local destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z)
SetBlipSprite(destinationBlip, 1)
SetBlipColour(destinationBlip, 2)
SetBlipScale(destinationBlip, 0.8)
2025-07-30 07:32:37 +02:00
SetBlipRoute(destinationBlip, true) -- Route zum Ziel anzeigen
2025-07-30 02:53:21 +02:00
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Taxi Ziel")
EndTextCommandSetBlipName(destinationBlip)
-- Route setzen
SetNewWaypoint(destination.x, destination.y)
return
end
2025-07-30 02:17:38 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Fahrt gestartet - Preis: $' .. price,
type = 'success'
})
-- Destination Blip erstellen
local destinationBlip = AddBlipForCoord(destination.x, destination.y, destination.z)
SetBlipSprite(destinationBlip, 1)
SetBlipColour(destinationBlip, 2)
SetBlipScale(destinationBlip, 0.8)
2025-07-30 07:32:37 +02:00
SetBlipRoute(destinationBlip, true) -- Route zum Ziel anzeigen
2025-07-30 02:17:38 +02:00
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Taxi Ziel")
EndTextCommandSetBlipName(destinationBlip)
2025-07-30 08:49:39 +02:00
-- Ziel im Fahrzeug-State speichern für die KI-Logik
Entity(vehicle).state.currentDestination = destination
2025-07-30 08:20:25 +02:00
2025-07-30 08:49:39 +02:00
-- Zum Ziel fahren mit verbesserter Navigation
local drivingStyle = 786603 -- Normal/Vorsichtig
TaskVehicleDriveToCoordLongrange(driver, vehicle, destination.x, destination.y, destination.z, 20.0, drivingStyle, 10.0)
-- Fahrer-Dialog anzeigen
local driverName = Entity(vehicle).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
})
-- Fahrt überwachen (vereinfacht, da KI-Logik die meiste Arbeit übernimmt)
CreateThread(function()
local rideTimeout = GetGameTimer() + (8 * 60 * 1000) -- 8 Minuten Timeout
2025-07-30 08:31:19 +02:00
2025-07-30 08:49:39 +02:00
while DoesEntityExist(vehicle) and DoesEntityExist(driver) do
local vehicleCoords = GetEntityCoords(vehicle)
local distance = #(vector3(destination.x, destination.y, destination.z) - vehicleCoords)
2025-07-30 08:31:19 +02:00
2025-07-30 08:49:39 +02:00
-- Überprüfen ob wir angekommen sind
if distance < 10.0 then
-- Angekommen
TaskVehicleTempAction(driver, vehicle, 27, 3000)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Arrived at destination")
2025-07-30 08:49:39 +02:00
lib.notify({
title = 'Taxi Service',
description = 'Du bist angekommen! Preis: $' .. price,
type = 'success'
})
-- Bezahlung
TriggerServerEvent('taxi:payFare', price)
-- Blip entfernen
RemoveBlip(destinationBlip)
-- Fahrer-Dialog anzeigen
local driverName = Entity(vehicle).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
})
-- Nach 10 Sekunden Taxi zurück zur Station
SetTimeout(10000, function()
ReturnTaxiToStation(stationId, vehicleId, vehicle, driver)
end)
break
2025-07-30 02:17:38 +02:00
end
2025-07-30 08:49:39 +02:00
-- Überprüfen ob die Fahrt zu lange dauert
if GetGameTimer() > rideTimeout then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Taxi ride timed out!")
2025-07-30 08:49:39 +02:00
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)
2025-07-30 07:32:37 +02:00
end
2025-07-30 08:49:39 +02:00
-- Teleportiere Taxi
SetEntityCoords(vehicle, nearDestination.x, nearDestination.y, nearDestination.z, false, false, false, false)
-- Neues Timeout setzen (1 Minute)
rideTimeout = GetGameTimer() + (60 * 1000)
2025-07-30 07:32:37 +02:00
end
2025-07-30 08:49:39 +02:00
Wait(2000)
2025-07-30 02:17:38 +02:00
end
2025-07-30 08:49:39 +02:00
end)
end
2025-07-30 02:17:38 +02:00
function ExitStationTaxi(stationId, vehicleId, vehicle, driver)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Player exiting station taxi")
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
local playerPed = PlayerPedId()
TaskLeaveVehicle(playerPed, vehicle, 0)
lib.notify({
title = 'Taxi Service',
description = 'Du bist ausgestiegen',
type = 'info'
})
-- Taxi zurück zur Station nach 5 Sekunden
SetTimeout(5000, function()
ReturnTaxiToStation(stationId, vehicleId, vehicle, driver)
end)
end
function ReturnTaxiToStation(stationId, vehicleId, vehicle, driver)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Returning taxi to station: " .. stationId .. "/" .. vehicleId)
2025-07-30 02:46:15 +02:00
2025-07-30 02:17:38 +02:00
if not stationVehicles[stationId] or not stationVehicles[stationId][vehicleId] then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Station vehicle data not found for return")
2025-07-30 02:17:38 +02:00
return
end
2025-07-30 03:50:38 +02:00
if not DoesEntityExist(vehicle) then
2025-07-31 07:35:56 +02:00
DebugPrint("^1[TAXI STATIONS DEBUG]^7 Vehicle doesn't exist anymore")
2025-07-30 03:50:38 +02:00
-- Fahrzeug als nicht besetzt markieren
stationVehicles[stationId][vehicleId].occupied = false
stationVehicles[stationId][vehicleId].driver = nil
stationVehicles[stationId][vehicleId].entity = nil
-- Nach Respawn-Zeit neues Fahrzeug spawnen
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Scheduling respawn in " .. Config.StationTaxiRespawnTime .. " seconds")
2025-07-30 03:50:38 +02:00
SetTimeout(Config.StationTaxiRespawnTime * 1000, function()
if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] then
local vehicleData = stationVehicles[stationId][vehicleId].data
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station")
2025-07-30 03:50:38 +02:00
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end
end)
return
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:50:38 +02:00
-- Wenn Fahrer existiert, Taxi zur Station zurückfahren lassen
if driver and DoesEntityExist(driver) then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Making taxi drive back to station")
2025-07-30 03:50:38 +02:00
-- Zufällige Position in der Nähe der Station finden
local stationCoords = Config.TaxiStations[stationId].coords
2025-07-30 08:49:39 +02:00
-- Taxi zur Station zurückfahren lassen mit geduldiger Fahrweise
local drivingStyle = 786603 -- Normal/Vorsichtig
TaskVehicleDriveToCoordLongrange(driver, vehicle, stationCoords.x, stationCoords.y, stationCoords.z, 20.0, drivingStyle, 10.0)
2025-07-30 03:50:38 +02:00
-- qb-target entfernen während der Fahrt
2025-07-30 02:17:38 +02:00
exports['qb-target']:RemoveTargetEntity(vehicle)
2025-07-30 03:50:38 +02:00
-- Nach 10 Sekunden tatsächlich löschen
SetTimeout(10000, function()
-- Fahrer löschen
if driver and DoesEntityExist(driver) then
DeleteEntity(driver)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Driver deleted")
2025-07-30 03:50:38 +02:00
end
-- Fahrzeug löschen
if DoesEntityExist(vehicle) then
DeleteEntity(vehicle)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Vehicle deleted")
2025-07-30 03:50:38 +02:00
end
-- Fahrzeug als nicht besetzt markieren
stationVehicles[stationId][vehicleId].occupied = false
stationVehicles[stationId][vehicleId].driver = nil
stationVehicles[stationId][vehicleId].entity = nil
-- Nach Respawn-Zeit neues Fahrzeug spawnen
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Scheduling respawn in " .. Config.StationTaxiRespawnTime .. " seconds")
2025-07-30 03:50:38 +02:00
SetTimeout(Config.StationTaxiRespawnTime * 1000, function()
if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] then
local vehicleData = stationVehicles[stationId][vehicleId].data
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station")
2025-07-30 03:50:38 +02:00
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end
end)
end)
else
-- Sofort löschen wenn kein Fahrer da ist
-- qb-target entfernen
if DoesEntityExist(vehicle) then
exports['qb-target']:RemoveTargetEntity(vehicle)
DeleteEntity(vehicle)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Vehicle deleted")
2025-07-30 02:17:38 +02:00
end
2025-07-30 03:50:38 +02:00
-- Fahrzeug als nicht besetzt markieren
stationVehicles[stationId][vehicleId].occupied = false
stationVehicles[stationId][vehicleId].driver = nil
stationVehicles[stationId][vehicleId].entity = nil
-- Nach Respawn-Zeit neues Fahrzeug spawnen
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Scheduling respawn in " .. Config.StationTaxiRespawnTime .. " seconds")
2025-07-30 03:50:38 +02:00
SetTimeout(Config.StationTaxiRespawnTime * 1000, function()
if stationVehicles[stationId] and stationVehicles[stationId][vehicleId] then
local vehicleData = stationVehicles[stationId][vehicleId].data
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Respawning vehicle at station")
2025-07-30 03:50:38 +02:00
SpawnStationVehicle(stationId, vehicleId, vehicleData)
end
end)
end
2025-07-30 02:17:38 +02:00
end
function CalculateDistanceToCoords(coords)
local playerCoords = GetEntityCoords(PlayerPedId())
return #(playerCoords - coords)
end
2025-07-30 02:59:59 +02:00
-- Command um nächste Taxi-Station zu finden
RegisterCommand('nearesttaxi', function()
2025-07-30 02:20:13 +02:00
local playerCoords = GetEntityCoords(PlayerPedId())
local nearestStation = nil
local nearestDistance = math.huge
for stationId, station in pairs(Config.TaxiStations) do
local distance = #(playerCoords - station.blipCoords)
if distance < nearestDistance then
nearestDistance = distance
nearestStation = {id = stationId, data = station, distance = distance}
end
end
2025-07-30 02:59:59 +02:00
if nearestStation then
2025-07-30 02:20:13 +02:00
lib.notify({
title = 'Taxi Service',
2025-07-30 03:50:38 +02:00
description = 'Nächste Station: ' .. nearestStation.data.name .. ' (' .. math.ceil(nearestDistance) .. 'm)',
2025-07-30 02:20:13 +02:00
type = 'info'
})
-- Waypoint zur nächsten Station setzen
2025-07-30 02:59:59 +02:00
SetNewWaypoint(nearestStation.data.blipCoords.x, nearestStation.data.blipCoords.y)
2025-07-30 02:20:13 +02:00
else
lib.notify({
title = 'Taxi Service',
description = 'Keine Taxi-Station gefunden',
type = 'error'
})
end
end)
2025-07-30 02:46:15 +02:00
-- Event für Admin Respawn
RegisterNetEvent('taxi:respawnAllStations', function()
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Respawning all stations...")
2025-07-30 02:20:13 +02:00
2025-07-30 02:46:15 +02:00
-- Alle bestehenden Fahrzeuge löschen
for stationId, vehicles in pairs(stationVehicles) do
for vehicleId, vehicleInfo in pairs(vehicles) do
if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then
exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity)
DeleteEntity(vehicleInfo.entity)
2025-07-30 02:20:13 +02:00
end
2025-07-30 02:46:15 +02:00
if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then
DeleteEntity(vehicleInfo.driver)
2025-07-30 02:20:13 +02:00
end
end
2025-07-30 02:46:15 +02:00
end
2025-07-30 02:20:13 +02:00
2025-07-30 02:46:15 +02:00
-- Alle Blips entfernen
for _, blip in pairs(stationBlips) do
RemoveBlip(blip)
2025-07-30 02:20:13 +02:00
end
2025-07-30 02:46:15 +02:00
-- Variablen zurücksetzen
stationVehicles = {}
stationBlips = {}
-- Stationen neu initialisieren
Wait(1000)
InitializeTaxiStations()
lib.notify({
title = 'Taxi Service',
2025-07-30 03:21:38 +02:00
description = 'Alle Taxi-Stationen wurden neu gespawnt',
2025-07-30 02:46:15 +02:00
type = 'success'
2025-07-30 02:20:13 +02:00
})
2025-07-30 02:46:15 +02:00
end)
2025-07-30 02:20:13 +02:00
2025-07-30 04:53:36 +02:00
-- Thread zum Überwachen der Tasten im Stations-Taxi
CreateThread(function()
while true do
Wait(0)
local playerPed = PlayerPedId()
local inStationTaxi = false
local currentStationTaxi = nil
local currentStationId = nil
local currentVehicleId = nil
local currentDriver = nil
local pricePerKm = 0
-- Prüfen ob Spieler in einem Stations-Taxi sitzt
for stationId, vehicles in pairs(stationVehicles) do
for vehicleId, vehicleInfo in pairs(vehicles) do
if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) and vehicleInfo.occupied then
if IsPedInVehicle(playerPed, vehicleInfo.entity, false) then
inStationTaxi = true
currentStationTaxi = vehicleInfo.entity
currentStationId = stationId
currentVehicleId = vehicleId
currentDriver = vehicleInfo.driver
pricePerKm = vehicleInfo.data.pricePerKm
break
end
end
end
if inStationTaxi then break end
end
if inStationTaxi and currentStationTaxi 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
OpenStationTaxiMenu(currentStationId, currentVehicleId, currentStationTaxi, currentDriver, pricePerKm)
end
-- Wenn F gedrückt wird, beende Fahrt
if IsControlJustReleased(0, 23) then -- F Taste
lib.hideTextUI()
EndStationTaxiRide(currentStationId, currentVehicleId, currentStationTaxi, currentDriver)
end
else
-- Nicht in einem Stations-Taxi
2025-07-30 07:32:37 +02:00
lib.hideTextUI()
2025-07-30 04:53:36 +02:00
Wait(1000)
end
end
end)
-- Funktion zum Beenden der Stations-Taxi Fahrt
function EndStationTaxiRide(stationId, vehicleId, vehicle, driver)
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Ending station taxi ride")
2025-07-30 04:53:36 +02:00
if not vehicle or not DoesEntityExist(vehicle) 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, vehicle, 0)
-- Warten bis ausgestiegen
CreateThread(function()
local timeout = GetGameTimer() + 5000
while GetGameTimer() < timeout do
if not IsPedInVehicle(playerPed, vehicle, false) then
-- Spieler ist ausgestiegen
break
end
Wait(100)
end
-- Taxi nach 5 Sekunden zurück zur Station
SetTimeout(5000, function()
ReturnTaxiToStation(stationId, vehicleId, vehicle, driver)
end)
end)
end
2025-07-30 07:48:51 +02:00
-- Cleanup beim Resource Stop
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Cleaning up stations...")
2025-07-30 07:48:51 +02:00
-- TextUI verstecken falls noch angezeigt
lib.hideTextUI()
-- Alle Station-Fahrzeuge löschen
for stationId, vehicles in pairs(stationVehicles) do
for vehicleId, vehicleInfo in pairs(vehicles) do
if vehicleInfo.entity and DoesEntityExist(vehicleInfo.entity) then
exports['qb-target']:RemoveTargetEntity(vehicleInfo.entity)
DeleteEntity(vehicleInfo.entity)
end
if vehicleInfo.driver and DoesEntityExist(vehicleInfo.driver) then
DeleteEntity(vehicleInfo.driver)
end
end
end
-- Alle Blips entfernen
for _, blip in pairs(stationBlips) do
RemoveBlip(blip)
end
2025-07-31 07:35:56 +02:00
DebugPrint("^2[TAXI STATIONS DEBUG]^7 Cleanup completed")
2025-07-30 07:48:51 +02:00
end
end)
2025-07-31 07:41:08 +02:00
2025-07-30 08:49:39 +02:00