--=================================================== -- Variables used BY the script, do NOT modify them -- unless you know what you are doing! -- Modifying these might/will result in undesired -- behaviour and/or script breaking! --=================================================== local IsPlayerNearMetro = false local IsPlayerInMetro = false local PlayerHasMetroTicket = false local IsPlayerUsingTicketMachine = false local ShowingExitMetroMessage = false local EverythingisK = false local Train = nil local MetroTrain = nil local MetroTrain2 = nil local Driver1 = nil local Driver2 = nil local Driver3 = nil local MetroTrainStopped = {} --=================================================== -- These are radius locations (multiple per station) -- to detect if the player can exit the Metro --=================================================== local XNLMetroScanPoints = { {XNLStationid=0, x=230.82389831543, y=-1204.0643310547, z=38.902523040771}, {XNLStationid=0, x=249.59216308594, y=-1204.7095947266, z=38.92488861084}, {XNLStationid=0, x=270.33166503906, y=-1204.5366210938, z=38.902912139893}, {XNLStationid=0, x=285.96697998047, y=-1204.2261962891, z=38.929733276367}, {XNLStationid=0, x=304.13528442383, y=-1204.3720703125, z=38.892612457275}, {XNLStationid=1, x=-294.53421020508, y=-353.38571166992, z=10.063089370728}, {XNLStationid=1, x=-294.96997070313, y=-335.69766235352, z=10.06309223175}, {XNLStationid=1, x=-294.66772460938, y=-318.29565429688, z=10.063152313232}, {XNLStationid=1, x=-294.73403930664, y=-303.77200317383, z=10.063160896301}, {XNLStationid=1, x=-294.84133911133, y=-296.04568481445, z=10.063159942627}, {XNLStationid=2, x=-795.28063964844, y=-126.3436050415, z=19.950298309326}, {XNLStationid=2, x=-811.87170410156, y=-136.16409301758, z=19.950319290161}, {XNLStationid=2, x=-819.25689697266, y=-140.25764465332, z=19.95037651062}, {XNLStationid=2, x=-826.06652832031, y=-143.90898132324, z=19.95037651062}, {XNLStationid=2, x=-839.2587890625, y=-151.32421875, z=19.950378417969}, {XNLStationid=2, x=-844.77874755859, y=-154.31440734863, z=19.950380325317}, {XNLStationid=3, x=-1366.642578125, y=-440.04803466797, z=15.045327186584}, {XNLStationid=3, x=-1361.4998779297, y=-446.50497436523, z=15.045324325562}, {XNLStationid=3, x=-1357.4061279297, y=-453.40963745117, z=15.045320510864}, {XNLStationid=3, x=-1353.4593505859, y=-461.88238525391, z=15.045323371887}, {XNLStationid=3, x=-1346.1264648438, y=-474.15142822266, z=15.045383453369}, {XNLStationid=3, x=-1338.1717529297, y=-488.97756958008, z=15.045383453369}, {XNLStationid=3, x=-1335.0261230469, y=-493.50796508789, z=15.045380592346}, {XNLStationid=4, x=-530.67529296875, y=-673.33935546875, z=11.808959960938}, {XNLStationid=4, x=-517.35559082031, y=-672.76635742188, z=11.808965682983}, {XNLStationid=4, x=-499.44836425781, y=-673.37664794922, z=11.808973312378}, {XNLStationid=4, x=-483.1321105957, y=-672.68438720703, z=11.809024810791}, {XNLStationid=4, x=-468.05545043945, y=-672.74371337891, z=11.80902671814}, {XNLStationid=5, x=-206.90379333496, y=-1014.9454345703, z=30.138082504272}, {XNLStationid=5, x=-212.65534973145, y=-1031.6101074219, z=30.208702087402}, {XNLStationid=5, x=-212.65534973145, y=-1031.6101074219, z=30.208702087402}, {XNLStationid=5, x=-217.0216217041, y=-1042.4768066406, z=30.573789596558}, {XNLStationid=5, x=-221.29409790039, y=-1054.5914306641, z=30.13950920105}, {XNLStationid=6, x=101.89681243896, y=-1714.7589111328, z=30.112174987793}, {XNLStationid=6, x=113.05246734619, y=-1724.7247314453, z=30.111650466919}, {XNLStationid=6, x=122.72943878174, y=-1731.7276611328, z=30.54141998291}, {XNLStationid=6, x=132.55198669434, y=-1739.7276611328, z=30.109527587891}, {XNLStationid=7, x=-532.24133300781, y=-1263.6896972656, z=26.901586532593}, {XNLStationid=7, x=-539.62115478516, y=-1280.5207519531, z=26.908163070679}, {XNLStationid=7, x=-545.18548583984, y=-1290.9525146484, z=26.901586532593}, {XNLStationid=7, x=-549.92230224609, y=-1302.8682861328, z=26.901605606079}, {XNLStationid=8, x=-872.75714111328, y=-2289.3198242188, z=-11.732793807983}, {XNLStationid=8, x=-875.53247070313, y=-2297.67578125, z=-11.732793807983}, {XNLStationid=8, x=-880.05035400391, y=-2309.1235351563, z=-11.732788085938}, {XNLStationid=8, x=-883.25482177734, y=-2321.3303222656, z=-11.732738494873}, {XNLStationid=8, x=-890.087890625, y=-2336.2553710938, z=-11.732738494873}, {XNLStationid=8, x=-894.92395019531, y=-2350.4128417969, z=-11.732727050781}, {XNLStationid=9, x=-1062.7882080078, y=-2690.7492675781, z=-7.4116077423096}, {XNLStationid=9, x=-1071.6839599609, y=-2701.8503417969, z=-7.410071849823}, {XNLStationid=9, x=-1079.0869140625, y=-2710.7033691406, z=-7.4100732803345}, {XNLStationid=9, x=-1086.8758544922, y=-2720.0673828125, z=-7.4101362228394}, {XNLStationid=9, x=-1095.3796386719, y=-2729.8442382813, z=-7.4101347923279}, {XNLStationid=9, x=-1103.7401123047, y=-2740.369140625, z=-7.4101300239563} } -- These are the 'exit points' to where the player is teleported with the short fade-out / fade-in -- NOTE: XNLStationid is NOT used in this table, it's just here for user refrence! local XNLMetroEXITPoints = { {XNLStationid=0, x=294.46011352539, y=-1203.5991210938, z=38.902496337891, h=90.168075561523}, {XNLStationid=1, x=-294.76913452148, y=-303.44619750977, z=10.063159942627, h=185.19216918945}, {XNLStationid=2, x=-839.20843505859, y=-151.43312072754, z=19.950380325317, h=298.70877075195}, {XNLStationid=3, x=-1337.9787597656, y=-488.36145019531, z=15.045375823975, h=28.487064361572}, {XNLStationid=4, x=-474.07037353516, y=-673.10729980469, z=11.809032440186, h=81.799621582031}, {XNLStationid=5, x=-222.13038635254, y=-1054.5043945313, z=30.139930725098, h=155.81954956055}, {XNLStationid=6, x=133.13328552246, y=-1739.5617675781, z=30.109495162964, h=231.40335083008}, {XNLStationid=7, x=-550.79998779297, y=-1302.4467773438, z=26.901605606079, h=155.53070068359}, {XNLStationid=8, x=-891.87664794922, y=-2342.6486816406, z=-11.732737541199, h=353.59387207031}, {XNLStationid=9, x=-1099.6376953125, y=-2734.8957519531, z=-7.410129070282, h=314.91424560547} } -- This is all our "Stop" station for the metro. local MetroTrainstops = { -- Los Santos AirPort (airport front door entrance) {x=-1088.627, y=-2709.362, z=-7.137033}, {x=-1081.309, y=-2725.259, z=-7.137033}, -- Los Santos AirPort (car park/highway entrance) {x=-889.2755, y=-2311.825, z=-11.45941}, {x=-876.7512, y=-2323.808, z=-11.45609}, -- Little Seoul (near los santos harbor) {x=-545.3138, y=-1280.548, z=27.09238}, {x=-536.8082, y=-1286.096, z=27.08238}, -- Strawberry (near strip club) {x=270.2029, y=-1210.818, z=39.25398}, {x=265.3616, y=-1198.051, z=39.23406}, -- Rockford Hills (San Vitus Blvd) {x=-286.3837, y=-318.877, z=10.33625}, {x=-302.6719, y=-322.995, z=10.33629}, -- Rockford Hills (Near golf club) {x=-826.3845, y=-134.7151, z=20.22362}, {x=-816.7159, y=-147.4567, z=20.2231}, -- Del Perro (Near beach) {x=-1351.282, y=-481.2916, z=15.318}, {x=-1341.085, y=-467.674, z=15.31838}, -- Little Seoul {x=-496.0209, y=-681.0325, z=12.08264}, {x=-495.8456, y=-665.4668, z=12.08244}, -- Pillbox Hill (Downtown) {x=-218.2868, y=-1031.54, z=30.51112}, {x=-209.6845, y=-1037.544, z=30.50939}, -- Davis (Gang / hood area) {x=112.3714, y=-1729.233, z=30.24097}, {x=120.0308, y=-1723.956, z=30.31433}, } local TicketMachines = {'prop_train_ticket_02', 'prop_train_ticket_02_tu', 'v_serv_tu_statio3_'} local anim = "mini@atmenter" Citizen.CreateThread(function() function LoadTrainModels() -- f*ck your rails, too! tempmodel = GetHashKey("freight") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("freightcar") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("freightgrain") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("freightcont1") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("freightcont2") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("freighttrailer") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("tankercar") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("metrotrain") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end tempmodel = GetHashKey("s_m_m_lsmetro_01") RequestModel(tempmodel) while not HasModelLoaded(tempmodel) do RequestModel(tempmodel) Citizen.Wait(0) end if Debug then if Debug then print("FiveM-Trains: Train Models Loaded" ) end end end LoadTrainModels() RegisterNetEvent("StartTrain") function StartTrain() randomSpawn = math.random(#TrainLocations) x,y,z = TrainLocations[randomSpawn][1], TrainLocations[randomSpawn][2], TrainLocations[randomSpawn][3] -- get some random locations for our spawn -- For those whom are interested: The yesorno variable determines the direction of the train ;) yesorno = math.random(0,100) if yesorno >= 50 then -- untested, but seems to work /shrug yesorno = true elseif yesorno < 50 then yesorno = false end Wait(100) Train = CreateMissionTrain(math.random(0,22), x,y,z,yesorno) if Debug then print("FiveM-Trains: Train 1 created (Freight)." ) end while not DoesEntityExist(Train) do Wait(800) if Debug then print("FiveM-Trains: Waiting for Freight to be created" ) end end if ShowTrainBlips then local TrainBlip = AddBlipForEntity(Train) SetBlipSprite (TrainBlip, 660) SetBlipDisplay(TrainBlip, 4) SetBlipScale (TrainBlip, 0.8) SetBlipColour (TrainBlip, 2) SetBlipAsShortRange(TrainBlip, true) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Train") EndTextCommandSetBlipName(TrainBlip) end SetTrainCruiseSpeed(Train, 20.0) Wait(200) -- Added a small 'waiting' while the train is loaded (to prevent the) -- random unexplained spawning of the freight train on the Metro Rails MetroTrain = CreateMissionTrain(24,40.2,-1201.3,31.0,true) -- these ones have pre-defined spawns since they are a pain to set up if Debug then print("FiveM-Trains: Train 2 created (Metro)." ) end while not DoesEntityExist(MetroTrain) do Wait(800) if Debug then print("FiveM-Trains: Waiting for Metro Train 1 to be created" ) end -- Also wait until the train entity has actually been created end if ShowTrainBlips then local MetroBlip = AddBlipForEntity(MetroTrain) SetBlipSprite (MetroBlip, 660) SetBlipDisplay(MetroBlip, 4) SetBlipScale (MetroBlip, 0.8) SetBlipColour (MetroBlip, 2) SetBlipAsShortRange(MetroBlip, true) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Metro") EndTextCommandSetBlipName(MetroBlip) end SetTrainCruiseSpeed(MetroTrain, 15.0) Wait(200) -- Added a small 'waiting' while the train is loaded (to prevent the) -- random unexplained spawning of the freight train on the Metro Rails if UseTwoMetros == 1 then MetroTrain2 = CreateMissionTrain(24,-618.0,-1476.8,16.2,true) if Debug then print("FiveM-Trains: Train 3 created (Metro #2)." ) end while not DoesEntityExist(MetroTrain2) do Wait(800) if Debug then print("FiveM-Trains: Waiting for Metro Train 2 to be created" ) end -- Also wait until the train entity has actually been created end if ShowTrainBlips then local Metro2Blip = AddBlipForEntity(MetroTrain2) SetBlipSprite (Metro2Blip, 660) SetBlipDisplay(Metro2Blip, 4) SetBlipScale (Metro2Blip, 0.8) SetBlipColour (Metro2Blip, 2) SetBlipAsShortRange(Metro2Blip, true) BeginTextCommandSetBlipName("STRING") AddTextComponentString("Metro") EndTextCommandSetBlipName(Metro2Blip) end SetTrainCruiseSpeed(MetroTrain2, 15.0) end Wait(200) -- Added a small 'waiting' while the train is loaded (to prevent the) -- random unexplained spawning of the freight train on the Metro Rails TrainDriverHash = GetHashKey("s_m_m_lsmetro_01") -- By making a refrence to the drivers we can call them further on to make them invincible for example. Driver1 = CreatePedInsideVehicle(Train, 26, TrainDriverHash, -1, 1, true) Driver2 = CreatePedInsideVehicle(MetroTrain, 26, TrainDriverHash, -1, 1, true) if UseTwoMetros == 1 then Driver3 = CreatePedInsideVehicle(MetroTrain2, 26, TrainDriverHash, -1, 1, true) -- create peds for the trains end --========================================================= -- XNL 'Addition': This SHOULD prevent the train driver(s) -- from getting shot or fleeing out of the train/tram when -- being targeted by the player. -- We have had several instances where the tram driver just -- teleported out of the tram to attack the player when it -- it was targeted (even without holding a weapon). -- I suspect that this behaviour is default in the game -- unless you override it. --========================================================= SetBlockingOfNonTemporaryEvents(driver1, true) SetPedFleeAttributes(driver1, 0, 0) SetEntityInvincible(driver1, true) SetEntityAsMissionEntity(Driver1, true) SetBlockingOfNonTemporaryEvents(Driver3, true) SetPedFleeAttributes(Driver3, 0, 0) SetEntityInvincible(Driver3, true) SetEntityAsMissionEntity(Driver3, true) SetEntityAsMissionEntity(Train,true,true) -- dunno if this does anything, just throwing it in for good measure SetEntityAsMissionEntity(MetroTrain,true,true) SetEntityInvincible(Train, true) SetEntityInvincible(MetroTrain, true) if UseTwoMetros == 1 then SetBlockingOfNonTemporaryEvents(Driver2, true) SetPedFleeAttributes(Driver2, 0, 0) SetEntityInvincible(Driver2, true) SetEntityAsMissionEntity(Driver2, true) SetEntityAsMissionEntity(MetroTrain2,true,true) SetEntityInvincible(MetroTrain2, true) end -- Cleanup from memory SetModelAsNoLongerNeeded(TrainDriverHash) if Debug then print("FiveM-Trains: Train System Started, you are currently 'host' for the trains." ) end end AddEventHandler("StartTrain", StartTrain) EverythingisK = true -- Added this because the Event isn't fully registered when the Event PlayerSpawned trigger. end) Citizen.CreateThread(function() ShowedBuyTicketHelper = false ShowedLeaveMetroHelper = false while true do Wait(10) if IsPlayerNearTicketMachine then if not IsPlayerUsingTicketMachine then if not ShowedBuyTicketHelper then DisplayHelpText(Message[Language]['buyticket'].." ($" .. TicketPrice .. ")") ShowedBuyTicketHelper = true end else ClearAllHelpMessages() DisableControlAction(0, 201, true) DisableControlAction(1, 201, true) end if IsControlJustPressed(0, 51) and PlayerHasMetroTicket then SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['already_got_ticket'], true) Wait(3500) -- To avoid people 'spamming themselves' with the message popup (3500ms is 'just enough' to take the fun out of it :P) end if IsControlJustPressed(0, 51) and not PlayerHasMetroTicket then IsPlayerUsingTicketMachine = true RequestAnimDict("mini@atmbase") RequestAnimDict(anim) while not HasAnimDictLoaded(anim) do Wait(1) end SetCurrentPedWeapon(playerPed, GetHashKey("weapon_unarmed"), true) TaskLookAtEntity(playerPed, currentTicketMachine, 2000, 2048, 2) Wait(500) TaskGoStraightToCoord(playerPed, TicketMX, TicketMY, TicketMZ, 0.1, 4000, GetEntityHeading(currentTicketMachine), 0.5) Wait(2000) TaskPlayAnim(playerPed, anim, "enter", 8.0, 1.0, -1, 0, 0.0, 0, 0, 0) RemoveAnimDict(animDict) Wait(4000) TaskPlayAnim(playerPed, "mini@atmbase", "base", 8.0, 1.0, -1, 0, 0.0, 0, 0, 0) RemoveAnimDict("mini@atmbase") Wait(500) PlaySoundFrontend(-1, "ATM_WINDOW", "HUD_FRONTEND_DEFAULT_SOUNDSET", true) RequestAnimDict("mini@atmexit") while not HasAnimDictLoaded("mini@atmexit") do Wait(1) end TaskPlayAnim(playerPed, "mini@atmexit", "exit", 8.0, 1.0, -1, 0, 0.0, 0, 0, 0) RemoveAnimDict("mini@atmexit") Wait(500) --===================================================================================== -- Put here the actual 'reader'/function that your server uses -- to calculate/get the players bank account saldo and cash money! -- Now they are just set 'hardcoded' to an high amount to make the -- script work for people whom don't read a single line of code -- and then instantly post "Hey, i can't even buy a ticket, the script is broken" :P -- -- Nope it's NOT broken, it just needs a BIT of adapting to your server and it's -- money handling. Since we use a custom banking system we have much different calls -- than others might have so i've decided to put it in here like this so that it -- functions for everyone when they want to test/try the script :) --===================================================================================== BankAmount = 10000 --StatGetInt("BANK_BALANCE",-1) PlayerCashAm = 10000 --StatGetInt("MP0_WALLET_BALANCE",-1) if PayWithBank == 1 then XNLUserMoney = BankAmount else XNLUserMoney = PlayerCashAm end --=================================================================== -- Please note, that despite if you make your players pay with -- cash or by bank, it will always show the selected bank popup -- if the player doesn't have enough cash (this is NOT a bug!) -- if you want/need it differently you can adapt the code bellow ;) --================================================================== if XNLUserMoney < TicketPrice then if UserBankIDi == 1 then -- Maze Bank BankIcon = "CHAR_BANK_MAZE" BankName = "Maze Bank" end if UserBankIDi == 2 then -- Bank Of Liberty BankIcon = "CHAR_BANK_BOL" BankName = "Bank Of Liberty" end if UserBankIDi == 3 then -- Fleeca (Default Fallback to!) BankIcon = "CHAR_BANK_FLEECA" BankName = "Fleeca Bank" end SMS_Message(BankIcon, BankName, Message[Language]['account_information'], Message[Language]['account_nomoney'], true) else if PayWithBank == 1 then -- Put YOUR code to deduct the amount from the players BANK account here -- 'Basic Example': PlayerBankMoney = PlayerBankMoney - TicketPrice else -- Put YOUR code to deduct the amount from the players CASH money account here -- 'Basic Example': PlayerCash = PlayerCash - TicketPrice end SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['ticket_purchased'], true) PlayerHasMetroTicket = true end IsPlayerUsingTicketMachine = false end else ShowedBuyTicketHelper = false end -- E/Action key (There will only be checked for trains when the player presses the action key) -- This Section is used to ENTER the Metro if IsControlJustPressed(0, 51) then playerPed = PlayerPedId() x,y,z = table.unpack(GetEntityCoords(playerPed, true)) IsPlayerInVehicle = IsPedInAnyVehicle(playerPed, true) SkipReEnterCheck = false if IsPlayerInMetro then if XNLCanPlayerExitTrain() then if not XNLTeleportPlayerToNearestMetroExit() then SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['stop_toolate'], true) end SkipReEnterCheck = true -- This variable is used to prevent the character from directly trying to re-enter the Metro after leaving it. else XNLGenMess = "Sir" if XNLIsPedFemale(playerPed) then XNLGenMess = "Miss" end SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['sorry'].." "..Message[Language][XNLGenMess].." "..Message[Language]['exit_metro_random'], true) end end --=============================================== -- Make sure the player is NOT in a vehicle and -- NOT already on the Metro --=============================================== if not IsPlayerNearMetro and not IsPlayerInMetro and not SkipReEnterCheck then if not IsPlayerInVehicle then local coordA = GetEntityCoords(GetPlayerPed(-1), 1) local coordB = GetOffsetFromEntityInWorldCoords(GetPlayerPed(-1), 0.0, 3.0, 0.0) local Metro = getVehicleInDirection(coordA, coordB) if DoesEntityExist(Metro) then if GetEntityModel(Metro) == GetHashKey("metrotrain") then if not PlayerHasMetroTicket then --========================================================================== -- Notify the player he/she needs to buy a ticket before entering the metro --========================================================================== SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['need_ticket'], true) else if IsPlayerWantedLevelGreater(PlayerId(), 0) and AllowEnterTrainWanted == 0 then --========================================================================== -- If the player's wanted level is greater than 0, he/she will be -- denied to ENTER the Metro. -- If he/she GETS WHILE wanted on the train, we will handle that furher on --========================================================================== SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['have_wantedlevel'], true) else CurrentMetro = Metro IsPlayerNearMetro = true if FreeWalk then MetroX, MetroY, MetroZ = table.unpack(GetOffsetFromEntityInWorldCoords(CurrentMetro, 0.0, 0.0, 0.0)) SetEntityCoordsNoOffset(PlayerPedId(), MetroX, MetroY, MetroZ + 2.0) else SetPedIntoVehicle(GetPlayerPed(-1), CurrentMetro, 1) end IsPlayerInMetro = true PlayerHasMetroTicket = false SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['entered_metro'], true) end end else IsPlayerNearMetro = false end else IsPlayerNearMetro = false end else if not DoesEntityExist(CurrentMetro) then IsPlayerNearMetro = false else if GetDistanceBetweenCoords(x,y,z, MetroX, MetroY, MetroZ, true) > 3.5 then IsPlayerNearMetro = false end end end end end --============================================================= -- Check if the player is in the Metro AND pressed the [E] key --============================================================= if IsPlayerInMetro then if ReportTerroristOnMetro == true then if GetPlayerWantedLevel(PlayerId()) < 4 then if IsPedShooting(GetPlayerPed(-1)) then SetPlayerWantedLevel(PlayerId(), 4, 0) SetPlayerWantedLevelNow(PlayerId(), 0) SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['terrorist'], true) end end end if not DoesEntityExist(CurrentMetro) then -- Not ANY clue on when this might happen haha, but it's a funny message and error handler in one :Phone_SoundSet_Default -- we have seen it happen once or so in MANY test rounds of the metro system that the metro just vanished, so this is to -- 'encounter' that POSSIBLE issue (which I presume has to do with de-syncing or so) IsPlayerNearMetro = false IsPlayerInMetro = false PlayerHasMetroTicket = true SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['no_metro_spawned'], true) else if IsPlayerInMetro then -- This will ensure that it will only show the 'how to leave metro' text while near/at a station if ShowingExitMetroMessage == true and not ShowedLeaveMetroHelper then DisplayHelpText("Press ~INPUT_CONTEXT~ to leave the metro") ShowedLeaveMetroHelper = true end -- This part detects if the player is further away than 15.0 units from the Metro he/she used MetroX, MetroY, MetroZ = table.unpack(GetOffsetFromEntityInWorldCoords(CurrentMetro, 0.0, 0.0, 0.0)) x,y,z = table.unpack(GetEntityCoords(playerPed, true)) if GetDistanceBetweenCoords(x,y,z, MetroX, MetroY, MetroZ, true) > 15.0 then IsPlayerNearMetro = false IsPlayerInMetro = false SMS_Message("CHAR_LS_TOURIST_BOARD", Message[Language]['los_santos_transit'], Message[Language]['tourist_information'], Message[Language]['travel_metro'], true) end end end end end end) Citizen.CreateThread(function() --======================================================================================= -- Note only do this 'check' every 550ms to prevent -- to much load in the game (taking in account many other scripts also running of course) --======================================================================================= ShowedEToEnterMetro = false while true do Wait(550) if IsPlayerInMetro then if XNLCanPlayerExitTrain() then ShowingExitMetroMessage = true else ShowingExitMetroMessage = false ShowedLeaveMetroHelper = false end ShowedEToEnterMetro = false end -- We only have to check this part if the player is NOT on the metro. if not IsPlayerInMetro then playerPed = PlayerPedId() IsPlayerInVehicle = IsPedInAnyVehicle(playerPed, true) -- And then ONLY check it if the player isn't in a vehicle either -- Note: The way i'm using the metro, the game doesn't recognize it as being -- on/in a vehicle. if not IsPlayerInVehicle then -- Yes, yes I know, the function is called 'XNLCanPlayerEXITTrain', but it -- is also used to detect if the player is at one of the stations on foot :) if PlayerHasMetroTicket and XNLCanPlayerExitTrain() then if not ShowedEToEnterMetro then DisplayHelpText(Message[Language]['press_to_enter']) ShowedEToEnterMetro = true end else ShowedEToEnterMetro = false end -- Only show the "Press [E] to buy...." message near the ticket machine if the player does NOT own a ticket already -- Do note that it IS possible to 'activate' the ticket machine again though (but will give a different message ;) ) x,y,z = table.unpack(GetEntityCoords(playerPed, true)) -- And then only need to keep checking (scanning cords) if the player is not near the Ticket Machine (anymore) if not IsPlayerNearTicketMachine then for k,v in pairs(TicketMachines) do TicketMachine = GetClosestObjectOfType(x, y, z, 0.75, GetHashKey(v), false) if DoesEntityExist(TicketMachine) then currentTicketMachine = TicketMachine TicketMX, TicketMY, TicketMZ = table.unpack(GetOffsetFromEntityInWorldCoords(TicketMachine, 0.0, -.85, 0.0)) IsPlayerNearTicketMachine = true end end else if not DoesEntityExist(currentTicketMachine) then IsPlayerNearTicketMachine = false -- If for some (weird) reasons the ticked machine (suddenly) else --doesn't exist anymore, tell the script that the player isn't near one anymore if GetDistanceBetweenCoords(x,y,z, TicketMX, TicketMY, TicketMZ, true) > 2.0 then IsPlayerNearTicketMachine = false -- And do the same if the player is more than a radius of 2.0 away from the ticket machine end end end end end end end) -- This is the function which is used to display 'SMS Style messages' -- If you need more/other icons to display, then make sure to check out: -- https://wiki.gtanet.work/index.php?title=Notification_Pictures -- YES YES, I KNOW! it's 'a competitor' :P but it's definitly a good -- resource for fellow modders :) function SMS_Message(NotiPic, SenderName, Subject, MessageText, PlaySound) SetNotificationTextEntry("STRING") AddTextComponentString(MessageText) SetNotificationBackgroundColor(140) SetNotificationMessage(NotiPic, NotiPic, true, 4, SenderName, Subject, MessageText) DrawNotification(false, true) if PlaySound then PlaySoundFrontend(GetSoundId(), "Text_Arrive_Tone", "Phone_SoundSet_Default", true) end end -- This is the text 'helper' which is used at the top left for messages like 'Press [E] to buy ticket ($25)' function DisplayHelpText(str) SetTextComponentFormat("STRING") AddTextComponentString(str) DisplayHelpTextFromStringLabel(0, 0, 1, -1) EndTextCommandDisplayHelp(0, 0, true, 2000) end -- Using a RayCast to detect if the player is trying to get into the train -- This is needed since it's not possible (yet) to detect the train model with -- the normal native calls function getVehicleInDirection(coordFrom, coordTo) local rayHandle = CastRayPointToPoint(coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 10, GetPlayerPed(-1), 0) local a, b, c, d, vehicle = GetRaycastResult(rayHandle) return vehicle end --============================================== -- Simple yet effective function to check if -- player is female or male (sine we only use -- mp_f_freemode_01 and mp_m_freemode_01 on our -- server) We need(ed) this function because for -- some weird reason IsPedMale had some issues -- with some scripts. --============================================== function XNLIsPedFemale(ped) if IsPedModel(ped, 'mp_f_freemode_01') then return true else return false end end function XNLCanPlayerExitTrain() playerPed = PlayerPedId() for _, item in pairs(XNLMetroScanPoints) do Px,Py,Pz = table.unpack(GetEntityCoords(playerPed, true)) if GetDistanceBetweenCoords(Px,Py,Pz, item.x, item.y, item.z, true) < StationsExitScanRadius then return true -- The function DID detected the player within one of the radius markers at the stations end end return false -- The function did NOT detected the player within one of the radius markers at the stations end function XNLTeleportPlayerToNearestMetroExit() playerPed = PlayerPedId() for _, item in pairs(XNLMetroScanPoints) do Px,Py,Pz = table.unpack(GetEntityCoords(playerPed, true)) if GetDistanceBetweenCoords(Px,Py,Pz, item.x, item.y, item.z, true) < StationsExitScanRadius then for _, item2 in pairs(XNLMetroEXITPoints) do if item.XNLStationid == item2.XNLStationid then DoScreenFadeOut(800) while not IsScreenFadedOut() do Wait(10) end XNLNewX = item2.x -- The 'new' Player X position XNLNewY = item2.y -- The 'new' Player Y position XNLNewZ = item2.z -- The 'new' Player Z position XNLNewH = item2.h -- The 'new' Player Heading Direction SetEntityCoordsNoOffset(PlayerPedId(), XNLNewX, XNLNewY, XNLNewZ) SetEntityHeading(PlayerPedId(), XNLNewH) DoScreenFadeIn(800) while not IsScreenFadedIn() do Wait(10) end return true end end end end return false -- The function did NOT detected the player within one of the radius markers at the stations end Citizen.CreateThread(function() while EverythingisK == false do Citizen.Wait(0) end while true do Citizen.Wait(0) local closest = 10 if DoesEntityExist(MetroTrain) then if not MetroTrainStopped[MetroTrain] then local coords = GetEntityCoords(MetroTrain) if coords then for k,v in ipairs(MetroTrainstops) do if GetDistanceBetweenCoords(coords, v.x, v.y, v.z, true) <= closest then StopTrain(MetroTrain) end end end end end if DoesEntityExist(MetroTrain2) then if not MetroTrainStopped[MetroTrain2] then local coords = GetEntityCoords(MetroTrain2) if coords then for k,v in ipairs(MetroTrainstops) do if GetDistanceBetweenCoords(coords, v.x, v.y, v.z, true) <= closest then StopTrain(MetroTrain2) end end end end end end end) function StopTrain(train) if (NetworkHasControlOfEntity(train)) then table.insert(MetroTrainStopped, train) SetTrainCruiseSpeed(train, 0.0) -- We stop the train Citizen.Wait(100) if train ~= nil then local stoppedTimer = GetGameTimer(); repeat Citizen.Wait(0) until GetEntitySpeed(train) <= 0 while (GetGameTimer() - stoppedTimer < (20 * 1000)) do -- We stop the train for 20 sec. With a Citizen.wait, our variable is destroyed. So we need to keep up. Citizen.Wait(0) end end Citizen.Wait(100) SetTrainCruiseSpeed(train, 15.0) -- Bye bye train ! local timer = GetGameTimer(); while (GetGameTimer() - timer < 5000) do -- After 5 sec, we can tell that train have "finished" their stop. removebyKey(MetroTrainStopped, train) Citizen.Wait(0) end end end -- Added for OneSync local firstspawn = 0 -- By default, Its the first spawn of the player. So, I don't recommend to restart the script with already player in the server. AddEventHandler('playerSpawned', function() while EverythingisK == false do Citizen.Wait(0) end -- The Event "StartTrain" is fully registered. We can continue now. if firstspawn == 0 then -- First spawn of the player ? Check if they are already trains TriggerServerEvent('FiveM-Trains:PlayerSpawned') firstspawn = 1 -- Just for making not trigger the event if he respawn after die. end end) -- This is added if we restart the ressource. -- Needed for the dev. May be I will remove it later. -- This remove all train AddEventHandler('onResourceStop', function(resourceName) if (GetCurrentResourceName() ~= resourceName) then return end DeleteMissionTrain(MetroTrain) DeleteMissionTrain(MetroTrain2) DeleteMissionTrain(Train) end)