1
0
Fork 0
forked from Simnation/Main
Main/resources/[freizeit]/nordi_ai_train/client.lua

447 lines
14 KiB
Lua
Raw Normal View History

2025-08-11 18:33:49 +02:00
local QBCore = exports['qb-core']:GetCoreObject()
local lib = exports.ox_lib
local PlayerData = {}
local nearbyTrains = {}
local currentTrain = nil
local isRiding = false
local cinemaCam = nil
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
PlayerData = QBCore.Functions.GetPlayerData()
CreateStationBlips()
end)
-- Bahnhof Blips erstellen
function CreateStationBlips()
for _, station in pairs(Config.TrainStations) do
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)
end
end
-- Zug spawnen
function SpawnTrainAtLocation(station)
local model = GetHashKey(Config.TrainCars.main)
RequestModel(model)
while not HasModelLoaded(model) do
Wait(500)
end
local train = CreateMissionTrain(24, station.coords.x, station.coords.y, station.coords.z, true)
if DoesEntityExist(train) then
SetEntityHeading(train, station.coords.w)
SetTrainSpeed(train, 0.0)
SetTrainCruiseSpeed(train, 0.0)
-- Waggons hinzufügen
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
if Config.Debug then
print("Zug gespawnt bei: " .. station.name)
end
return train
end
return nil
end
-- Züge in der Nähe finden
function FindNearbyTrains()
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
local trains = {}
local vehicles = GetGamePool('CVehicle')
for _, vehicle in pairs(vehicles) do
if DoesEntityExist(vehicle) then
local model = GetEntityModel(vehicle)
if IsThisModelATrain(model) then
local trainCoords = GetEntityCoords(vehicle)
local distance = #(playerCoords - trainCoords)
if distance <= Config.InteractionDistance then
table.insert(trains, {
entity = vehicle,
coords = trainCoords,
distance = distance
})
end
end
end
end
return trains
end
-- ox_lib Zielmenü öffnen
function OpenDestinationMenu(train)
local playerCoords = GetEntityCoords(PlayerPedId())
local currentStation = GetNearestStation(playerCoords)
local options = {}
for _, station in pairs(Config.TrainStations) do
if station.name ~= currentStation then
local icon = Config.Menu.stationIcons.city -- Standard Icon
-- Icon basierend auf Station wählen
if string.find(station.name:lower(), "depot") then
icon = Config.Menu.stationIcons.depot
elseif string.find(station.name:lower(), "island") or string.find(station.name:lower(), "terminal") then
icon = Config.Menu.stationIcons.port
elseif string.find(station.name:lower(), "industrial") then
icon = Config.Menu.stationIcons.industrial
elseif string.find(station.name:lower(), "bay") then
icon = Config.Menu.stationIcons.rural
end
table.insert(options, {
title = icon .. " " .. station.name,
description = station.description .. " - " .. Config.Menu.texts.price .. ": $" .. station.price,
icon = 'train',
onSelect = function()
SelectDestination(train, station)
end,
metadata = {
{label = Config.Menu.texts.price, value = "$" .. station.price},
{label = "Entfernung", value = math.floor(#(playerCoords - vector3(station.coords.x, station.coords.y, station.coords.z))) .. "m"}
}
})
end
end
table.insert(options, {
title = "" .. Config.Menu.texts.cancel,
description = "Menü schließen",
icon = 'xmark'
})
lib:registerContext({
id = 'train_destination_menu',
title = Config.Menu.title,
options = options,
position = Config.Menu.position
})
lib:showContext('train_destination_menu')
end
-- Ziel auswählen
function SelectDestination(train, destination)
if not DoesEntityExist(train) then
lib:notify({
title = 'Fehler',
description = Config.Menu.texts.trainNotAvailable,
type = Config.Notifications.types.error,
duration = Config.Notifications.duration.short
})
return
end
-- Geld prüfen
QBCore.Functions.TriggerCallback('train:server:canAfford', function(canAfford)
if canAfford then
StartTrainJourney(train, destination)
else
lib:notify({
title = 'Nicht genug Geld',
description = Config.Menu.texts.notEnoughMoney .. " ($" .. destination.price .. ")",
type = Config.Notifications.types.error,
duration = Config.Notifications.duration.medium
})
end
end, destination.price)
end
-- Nächste Station finden
function GetNearestStation(coords)
local nearestStation = nil
local nearestDistance = math.huge
for _, station in pairs(Config.TrainStations) do
local distance = #(coords - vector3(station.coords.x, station.coords.y, station.coords.z))
if distance < nearestDistance then
nearestDistance = distance
nearestStation = station.name
end
end
return nearestStation
end
-- Zugfahrt starten
function StartTrainJourney(train, destination)
local playerPed = PlayerPedId()
currentTrain = train
isRiding = true
-- Spieler in Zug setzen
SetPedIntoVehicle(playerPed, train, 1)
-- Benachrichtigung
lib:notify({
title = '🚂 ' .. Config.Menu.texts.journeyStarted,
description = "Fahrt nach " .. destination.name,
type = Config.Notifications.types.success,
duration = Config.Notifications.duration.medium
})
-- Cinema Kamera starten
if Config.CinemaCamera.enabled then
StartCinemaCamera(train)
end
-- Automatische Fahrt
CreateThread(function()
Wait(3000)
lib:notify({
title = '🚂 ' .. Config.Menu.texts.trainDeparting,
description = "Nächster Halt: " .. destination.name,
type = Config.Notifications.types.info,
duration = Config.Notifications.duration.short
})
DriveTrainToDestination(train, destination)
end)
-- Journey loggen
if Config.DebugOptions.logJourneys then
TriggerServerEvent('train:server:logJourney', GetNearestStation(GetEntityCoords(playerPed)), destination.name, destination.price)
end
end
-- Cinema Kamera
function StartCinemaCamera(train)
if cinemaCam then
DestroyCam(cinemaCam, false)
end
CreateThread(function()
while isRiding and DoesEntityExist(train) do
for _, camPos in pairs(Config.CinemaCamera.positions) do
if not isRiding then break end
if cinemaCam then
DestroyCam(cinemaCam, false)
end
local trainCoords = GetEntityCoords(train)
local trainHeading = GetEntityHeading(train)
local camCoords = trainCoords + camPos.offset
cinemaCam = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
SetCamCoord(cinemaCam, camCoords.x, camCoords.y, camCoords.z)
SetCamRot(cinemaCam, camPos.rotation.x, camPos.rotation.y, camPos.rotation.z + trainHeading, 2)
SetCamActive(cinemaCam, true)
RenderScriptCams(true, true, 1000, true, true)
local holdTime = math.random(Config.CinemaCamera.switchInterval.min, Config.CinemaCamera.switchInterval.max)
Wait(holdTime)
end
end
end)
end
-- Zug zur Destination fahren
function DriveTrainToDestination(train, destination)
CreateThread(function()
local targetCoords = vector3(destination.coords.x, destination.coords.y, destination.coords.z)
local arrived = false
-- Beschleunigung
for speed = 0, Config.TrainSpeed.max, Config.TrainSpeed.acceleration do
if not DoesEntityExist(train) then return end
SetTrainSpeed(train, speed)
SetTrainCruiseSpeed(train, speed)
Wait(500)
end
-- Fahrt
while not arrived and DoesEntityExist(train) and isRiding do
local trainCoords = GetEntityCoords(train)
local distance = #(trainCoords - targetCoords)
if distance < 200 then
local newSpeed = math.max(Config.TrainSpeed.min, distance / 20)
SetTrainSpeed(train, newSpeed)
SetTrainCruiseSpeed(train, newSpeed)
end
if distance < 50 then
arrived = true
SetTrainSpeed(train, 0)
SetTrainCruiseSpeed(train, 0)
Wait(2000)
lib:notify({
title = '🚂 ' .. Config.Menu.texts.arrived,
description = destination.name,
type = Config.Notifications.types.success,
duration = Config.Notifications.duration.medium
})
lib:notify({
title = Config.Menu.texts.thankYou,
description = "Gute Weiterreise!",
type = Config.Notifications.types.info,
duration = Config.Notifications.duration.short
})
EndTrainJourney()
end
Wait(1000)
end
end)
end
-- Zugfahrt beenden
function EndTrainJourney()
isRiding = false
if cinemaCam then
RenderScriptCams(false, true, 1000, true, true)
DestroyCam(cinemaCam, false)
cinemaCam = nil
end
if currentTrain and DoesEntityExist(currentTrain) then
local playerPed = PlayerPedId()
TaskLeaveVehicle(playerPed, currentTrain, 0)
end
currentTrain = nil
Wait(3000)
lib:notify({
title = Config.Menu.texts.canExit,
type = Config.Notifications.types.info,
duration = Config.Notifications.duration.short
})
end
-- 3D Text zeichnen
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
-- Haupt-Loop
CreateThread(function()
while true do
local sleep = 1000
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
if not isRiding and not IsPedInAnyVehicle(playerPed, false) then
nearbyTrains = FindNearbyTrains()
for _, train in pairs(nearbyTrains) do
if train.distance <= 5.0 then
sleep = 0
DrawText3D(train.coords.x, train.coords.y, train.coords.z + 2.0, Config.DrawText.interactText)
if IsControlJustPressed(0, 38) then -- E
OpenDestinationMenu(train.entity)
end
end
end
end
if isRiding then
sleep = 0
if IsControlJustPressed(0, 23) then -- F
lib:notify({
title = Config.Menu.texts.emergencyExit,
type = Config.Notifications.types.warning,
duration = Config.Notifications.duration.short
})
EndTrainJourney()
end
end
Wait(sleep)
end
end)
-- Commands
RegisterCommand('spawntrain', function(source, args)
local stationId = args[1] or Config.TrainStations[1].id
local station = nil
for _, s in pairs(Config.TrainStations) do
if s.id == stationId then
station = s
break
end
end
if station then
SpawnTrainAtLocation(station)
lib:notify({
title = 'Zug gespawnt',
description = station.name,
type = Config.Notifications.types.success
})
end
end)
-- Auto-Spawn
if Config.AutoSpawn.enabled then
CreateThread(function()
Wait(Config.AutoSpawn.delay)
for i = 1, Config.AutoSpawn.maxTrains do
local randomStation = Config.TrainStations[math.random(1, #Config.TrainStations)]
SpawnTrainAtLocation(randomStation)
Wait(Config.AutoSpawn.spawnInterval)
end
end)
end
-- Cleanup
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
if isRiding then
EndTrainJourney()
end
end
end)