local QBCore = exports['qb-core']:GetCoreObject() local lib = exports.ox_lib local PlayerData = {} -- Zug Status local currentTrain = nil local isRiding = false local trainAtStation = {} local cinemaCam = nil local waitingForTrain = false -- Beim Laden des Scripts CreateThread(function() Wait(1000) -- Kurz warten bis alles geladen ist CreateStationBlips() print("^2[TRAIN] Blips erstellt^7") end) RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() PlayerData = QBCore.Functions.GetPlayerData() Wait(2000) CreateStationBlips() print("^2[TRAIN] Player loaded - Blips erstellt^7") end) -- Bahnhof Blips erstellen function CreateStationBlips() print("^3[TRAIN] Erstelle Blips für " .. #Config.TrainStations .. " Bahnhöfe^7") for i, station in pairs(Config.TrainStations) do print("^3[TRAIN] Erstelle Blip für: " .. station.name .. " bei " .. station.coords.x .. ", " .. station.coords.y .. "^7") local blip = AddBlipForCoord(station.coords.x, station.coords.y, station.coords.z) SetBlipSprite(blip, station.blip.sprite) SetBlipDisplay(blip, 4) SetBlipScale(blip, station.blip.scale) SetBlipColour(blip, station.blip.color) BeginTextCommandSetBlipName("STRING") AddTextComponentString("🚂 " .. station.name) EndTextCommandSetBlipName(blip) print("^2[TRAIN] Blip erstellt für: " .. station.name .. "^7") end end -- Bahnhof Interaktionen - Haupt-Loop CreateThread(function() print("^2[TRAIN] Interaktions-Loop gestartet^7") while true do local sleep = 1000 local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) if not isRiding then for i, station in pairs(Config.TrainStations) do local interactionPoint = station.interactionPoint or vector3(station.coords.x, station.coords.y, station.coords.z) local distance = #(playerCoords - interactionPoint) if distance <= Config.StationInteractionDistance then sleep = 0 -- DrawText anzeigen DrawText3D(interactionPoint.x, interactionPoint.y, interactionPoint.z + 1.0, Config.DrawText.stationText) -- Debug Info if Config.Debug then DrawText3D(interactionPoint.x, interactionPoint.y, interactionPoint.z + 2.0, "Station: " .. station.name .. " | Dist: " .. math.floor(distance)) end -- Interaktion if IsControlJustPressed(0, 38) then -- E print("^3[TRAIN] E gedrückt bei Station: " .. station.name .. "^7") if not waitingForTrain then OpenStationMenu(station) else lib:notify({ title = 'Bitte warten', description = 'Ein Zug ist bereits unterwegs', type = 'warning' }) end end end -- Prüfen ob Zug am Bahnhof wartet if trainAtStation[station.id] then local train = trainAtStation[station.id] if DoesEntityExist(train) then local trainCoords = GetEntityCoords(train) local trainDistance = #(playerCoords - trainCoords) if trainDistance <= 8.0 then sleep = 0 DrawText3D(trainCoords.x, trainCoords.y, trainCoords.z + 2.0, Config.DrawText.boardText) if IsControlJustPressed(0, 38) then -- E print("^3[TRAIN] Einsteigen in Zug^7") BoardTrain(train, station) end end end end end end Wait(sleep) end end) -- Bahnhof Menü öffnen function OpenStationMenu(station) print("^3[TRAIN] Öffne Menü für Station: " .. station.name .. "^7") local options = {} -- Verfügbare Ziele anzeigen if station.destinations then for _, destinationId in pairs(station.destinations) do local destination = GetStationById(destinationId) if destination then local price = CalculatePrice(station, destination) local icon = GetStationIcon(destination.name) table.insert(options, { title = icon .. " " .. destination.name, description = destination.description .. " - Preis: $" .. price, icon = 'train', onSelect = function() print("^3[TRAIN] Ziel gewählt: " .. destination.name .. "^7") CallTrainToStation(station, destination, price) end, metadata = { {label = "Preis", value = "$" .. price}, {label = "Entfernung", value = math.floor(GetDistanceBetweenStations(station, destination)) .. "m"} } }) end end end if #options == 0 then table.insert(options, { title = "Keine Ziele verfügbar", description = "Momentan keine Verbindungen", disabled = true }) end table.insert(options, { title = "❌ Abbrechen", description = "Menü schließen", icon = 'xmark' }) lib:registerContext({ id = 'station_menu', title = "🚉 " .. station.name, options = options, position = Config.Menu.position }) lib:showContext('station_menu') end -- Zug zum Bahnhof rufen function CallTrainToStation(station, destination, price) print("^3[TRAIN] Rufe Zug für " .. station.name .. " -> " .. destination.name .. "^7") -- Geld prüfen QBCore.Functions.TriggerCallback('train:server:canAfford', function(canAfford) if canAfford then waitingForTrain = true lib:notify({ title = '🚂 ' .. Config.Menu.texts.trainComing, description = 'Der Zug fährt zum Bahnhof', type = 'info', duration = Config.Notifications.duration.medium }) -- Zug spawnen und heranfahren lassen SpawnAndMoveTrainToStation(station, destination) else lib:notify({ title = 'Nicht genug Geld', description = "Benötigt: $" .. price, type = 'error', duration = Config.Notifications.duration.short }) end end, price) end -- Zug spawnen und zum Bahnhof fahren lassen function SpawnAndMoveTrainToStation(station, destination) CreateThread(function() print("^3[TRAIN] Spawne Zug für Station: " .. station.name .. "^7") -- Spawn-Punkt bestimmen local spawnPoint = station.trainSpawnPoint or vector4(station.coords.x - 500, station.coords.y, station.coords.z, station.coords.w) -- Zug am Spawn-Punkt erstellen local train = SpawnTrainAtLocation(spawnPoint) if not train then waitingForTrain = false lib:notify({ title = 'Fehler', description = 'Zug konnte nicht gespawnt werden', type = 'error' }) print("^1[TRAIN] Fehler beim Spawnen des Zugs^7") return end print("^2[TRAIN] Zug gespawnt, ID: " .. train .. "^7") -- Zug zum Bahnhof fahren lassen local targetCoords = vector3(station.coords.x, station.coords.y, station.coords.z) local arrived = false -- Zug in Bewegung setzen SetTrainSpeed(train, Config.TrainArrival.approachSpeed) SetTrainCruiseSpeed(train, Config.TrainArrival.approachSpeed) lib:notify({ title = '🚂 ' .. Config.Menu.texts.trainArriving, description = 'Der Zug nähert sich dem Bahnhof', type = 'info', duration = Config.Notifications.duration.short }) -- Warten bis Zug am Bahnhof ankommt while not arrived and DoesEntityExist(train) do local trainCoords = GetEntityCoords(train) local distance = #(trainCoords - targetCoords) if Config.Debug then print("^3[TRAIN] Zug Entfernung zum Bahnhof: " .. math.floor(distance) .. "m^7") end if distance < 100 then -- Langsamer werden SetTrainSpeed(train, Config.TrainArrival.arrivalSpeed) SetTrainCruiseSpeed(train, Config.TrainArrival.arrivalSpeed) end if distance < 30 then -- Ankunft SetTrainSpeed(train, 0) SetTrainCruiseSpeed(train, 0) arrived = true print("^2[TRAIN] Zug angekommen am Bahnhof: " .. station.name .. "^7") -- Zug am Bahnhof registrieren trainAtStation[station.id] = train waitingForTrain = false lib:notify({ title = '🚂 ' .. Config.Menu.texts.trainWaiting, description = 'Zug wartet am Bahnhof - [E] zum Einsteigen', type = 'success', duration = Config.Notifications.duration.long }) -- Zug nach Wartezeit entfernen wenn niemand einsteigt if Config.TrainArrival.despawnAfterWait then SetTimeout(Config.TrainArrival.waitTime, function() if trainAtStation[station.id] == train and not isRiding then print("^3[TRAIN] Zug fährt ab - keine Passagiere^7") lib:notify({ title = '🚂 Zug fährt ab', description = 'Der Zug verlässt den Bahnhof', type = 'warning', duration = Config.Notifications.duration.short }) -- Zug wegfahren lassen SetTrainSpeed(train, 15.0) SetTrainCruiseSpeed(train, 15.0) SetTimeout(10000, function() if DoesEntityExist(train) then DeleteMissionTrain(train) end end) trainAtStation[station.id] = nil end end) end end Wait(1000) end end) end -- In Zug einsteigen function BoardTrain(train, station) local playerPed = PlayerPedId() SetPedIntoVehicle(playerPed, train, 1) isRiding = true currentTrain = train lib:notify({ title = '🚂 Willkommen an Bord', description = 'Die Fahrt beginnt in Kürze', type = 'success', duration = Config.Notifications.duration.medium }) -- Hier würde die normale Zugfahrt-Logik weitergehen -- Für jetzt einfach nach 10 Sekunden wieder aussteigen lassen SetTimeout(10000, function() TaskLeaveVehicle(playerPed, train, 0) isRiding = false currentTrain = nil trainAtStation[station.id] = nil lib:notify({ title = '🚂 Ankunft', description = 'Sie sind angekommen', type = 'success' }) -- Zug wegfahren lassen SetTrainSpeed(train, 15.0) SetTimeout(5000, function() if DoesEntityExist(train) then DeleteMissionTrain(train) end end) end) end -- Zug spawnen function SpawnTrainAtLocation(spawnPoint) local model = GetHashKey(Config.TrainCars.main) RequestModel(model) while not HasModelLoaded(model) do Wait(500) end local train = CreateMissionTrain(24, spawnPoint.x, spawnPoint.y, spawnPoint.z, true) if DoesEntityExist(train) then SetEntityHeading(train, spawnPoint.w) SetTrainSpeed(train, 0.0) SetTrainCruiseSpeed(train, 0.0) -- Waggons hinzufügen CreateThread(function() Wait(1000) for _, carModel in pairs(Config.TrainCars.cars) do local carHash = GetHashKey(carModel) RequestModel(carHash) while not HasModelLoaded(carHash) do Wait(500) end CreateMissionTrainCar(train, carHash, false, false, false) end end) return train end return nil end -- Hilfsfunktionen function GetStationById(id) for _, station in pairs(Config.TrainStations) do if station.id == id then return station end end return nil end function CalculatePrice(fromStation, toStation) local distance = GetDistanceBetweenStations(fromStation, toStation) local price = Config.PriceCalculation.basePrice + (distance * Config.PriceCalculation.pricePerKm / 100) -- Kostenlose Stationen prüfen for _, freeStation in pairs(Config.PriceCalculation.freeStations) do if fromStation.id == freeStation then price = price * 0.5 -- 50% Rabatt end end return math.min(math.floor(price), Config.PriceCalculation.maxPrice) end function GetDistanceBetweenStations(station1, station2) local coords1 = vector3(station1.coords.x, station1.coords.y, station1.coords.z) local coords2 = vector3(station2.coords.x, station2.coords.y, station2.coords.z) return #(coords1 - coords2) end function GetStationIcon(stationName) if string.find(stationName:lower(), "depot") then return Config.Menu.stationIcons.depot elseif string.find(stationName:lower(), "island") then return Config.Menu.stationIcons.port elseif string.find(stationName:lower(), "terminal") then return Config.Menu.stationIcons.industrial elseif string.find(stationName:lower(), "bay") then return Config.Menu.stationIcons.rural elseif string.find(stationName:lower(), "hauptbahnhof") then return Config.Menu.stationIcons.main else return Config.Menu.stationIcons.city end end function DrawText3D(x, y, z, text) local onScreen, _x, _y = World3dToScreen2d(x, y, z) if onScreen then SetTextScale(Config.DrawText.scale, Config.DrawText.scale) SetTextFont(Config.DrawText.font) SetTextProportional(1) SetTextColour(Config.DrawText.color.r, Config.DrawText.color.g, Config.DrawText.color.b, Config.DrawText.color.a) SetTextEntry("STRING") SetTextCentre(1) AddTextComponentString(text) DrawText(_x, _y) local factor = (string.len(text)) / 370 DrawRect(_x, _y + 0.0125, 0.015 + factor, 0.03, Config.DrawText.backgroundColor.r, Config.DrawText.backgroundColor.g, Config.DrawText.backgroundColor.b, Config.DrawText.backgroundColor.a) end end -- Debug Commands RegisterCommand('trainblips', function() CreateStationBlips() print("^2[TRAIN] Blips neu erstellt^7") end) RegisterCommand('traintest', function() local playerCoords = GetEntityCoords(PlayerPedId()) print("^3[TRAIN] Player Position: " .. playerCoords.x .. ", " .. playerCoords.y .. ", " .. playerCoords.z .. "^7") for i, station in pairs(Config.TrainStations) do local distance = #(playerCoords - vector3(station.coords.x, station.coords.y, station.coords.z)) print("^3[TRAIN] " .. station.name .. " - Entfernung: " .. math.floor(distance) .. "m^7") end end) -- Cleanup AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then for stationId, train in pairs(trainAtStation) do if DoesEntityExist(train) then DeleteMissionTrain(train) end end end end)