├── core ├── server.lua ├── shared.lua └── client.lua ├── FUNDING.yml ├── bridge ├── standalone │ ├── client.lua │ └── server.lua ├── esx │ ├── client.lua │ └── server.lua └── qb │ ├── client.lua │ └── server.lua ├── modules ├── tracker │ ├── server.lua │ └── client.lua ├── missions │ ├── server.lua │ └── client.lua └── airports │ ├── server.lua │ └── client.lua ├── fxmanifest.lua ├── __INSTALLATION └── Jobs │ ├── qbcore.lua │ └── esx.sql ├── README.md └── config.lua /core/server.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://www.buymeacoffee.com/PickleMods"] 2 | -------------------------------------------------------------------------------- /bridge/standalone/client.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') == 'started' then return end 2 | if GetResourceState('qb-core') == 'started' then return end 3 | 4 | print("You are not using a supported framework, it will be required to make edits to the bridge files.") -------------------------------------------------------------------------------- /bridge/standalone/server.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') == 'started' then return end 2 | if GetResourceState('qb-core') == 'started' then return end 3 | 4 | print("You are not using a supported framework, it will be required to make edits to the bridge files.") -------------------------------------------------------------------------------- /modules/tracker/server.lua: -------------------------------------------------------------------------------- 1 | TrackedAircraft = {} 2 | 3 | function UpdateTracker(source, data) 4 | if (data) then 5 | TrackedAircraft[source] = data 6 | else 7 | TrackedAircraft[source] = nil 8 | end 9 | TriggerClientEvent("pickle_airport:tracker:receiveAircraftUpdate", -1, source, TrackedAircraft[source]) 10 | end 11 | 12 | RegisterNetEvent("pickle_airport:tracker:updateAircraft", function(data) 13 | local source = source 14 | UpdateTracker(source, data) 15 | end) -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version "cerulean" 2 | game "gta5" 3 | version "1.1.2" 4 | shared_scripts { 5 | "@ox_lib/init.lua", 6 | "config.lua", 7 | "bridge/**/shared.lua", 8 | "modules/**/shared.lua", 9 | "core/shared.lua" 10 | } 11 | 12 | client_scripts { 13 | "bridge/**/client.lua", 14 | "modules/**/client.lua", 15 | "core/client.lua" 16 | } 17 | 18 | server_scripts { 19 | "bridge/**/server.lua", 20 | "modules/**/server.lua", 21 | "core/server.lua" 22 | } 23 | 24 | lua54 'yes' 25 | -------------------------------------------------------------------------------- /modules/missions/server.lua: -------------------------------------------------------------------------------- 1 | RegisterNetEvent("pickle_airport:finishedMission", function(index) 2 | local source = source 3 | if (PermissionCheck(source, "pilot_mission")) then 4 | local data = Config.Missions.Sequences[index] 5 | local rewards = data.Rewards 6 | for i=1, #rewards do 7 | local amount = GetRandomInt(rewards[i].min, rewards[i].max) 8 | AddItem(source, rewards[i].name, amount) 9 | end 10 | else 11 | ShowNotification(source, U.permission_denied) 12 | end 13 | end) -------------------------------------------------------------------------------- /__INSTALLATION/Jobs/qbcore.lua: -------------------------------------------------------------------------------- 1 | ['airport'] = { 2 | label = 'Airport', 3 | defaultDuty = true, 4 | offDutyPay = false, 5 | grades = { 6 | ['0'] = { 7 | name = 'Recruit', 8 | payment = 0 9 | }, 10 | ['1'] = { 11 | name = 'Pilot', 12 | payment = 0 13 | }, 14 | ['2'] = { 15 | name = 'Lead Pilot', 16 | payment = 0 17 | }, 18 | ['3'] = { 19 | name = 'Air-Traffic Controller', 20 | isboss = true, 21 | bankAuth = true, 22 | payment = 0 23 | }, 24 | }, 25 | }, -------------------------------------------------------------------------------- /core/shared.lua: -------------------------------------------------------------------------------- 1 | function lerp(a, b, t) return a + (b-a) * t end 2 | 3 | function v3(coords) return vec3(coords.x, coords.y, coords.z), coords.w end 4 | 5 | function dprint(...) 6 | if Config.Debug then 7 | print(...) 8 | end 9 | end 10 | 11 | function GetRandomInt(min, max, skipNum) 12 | if min >= max then 13 | return min 14 | else 15 | local num = math.random(min, max) 16 | if skipNum ~= nil and skipNum == num then 17 | while skipNum == num do 18 | num = math.random(min, max) 19 | Wait(0) 20 | end 21 | end 22 | return num 23 | end 24 | end -------------------------------------------------------------------------------- /__INSTALLATION/Jobs/esx.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `jobs` (`name`, `label`, `whitelisted`) VALUES ('airport', 'Airport', '1'); 2 | 3 | INSERT INTO `job_grades` (`job_name`, `grade`, `name`, `label`, `salary`, `skin_male`, `skin_female`) VALUES ('airport', '0', 'recruit', 'Recruit', '0', '{}', '{}'); 4 | INSERT INTO `job_grades` (`job_name`, `grade`, `name`, `label`, `salary`, `skin_male`, `skin_female`) VALUES ('airport', '0', 'employee', 'Pilot', '0', '{}', '{}'); 5 | INSERT INTO `job_grades` (`job_name`, `grade`, `name`, `label`, `salary`, `skin_male`, `skin_female`) VALUES ('airport', '0', 'manager', 'Lead Pilot', '0', '{}', '{}'); 6 | INSERT INTO `job_grades` (`job_name`, `grade`, `name`, `label`, `salary`, `skin_male`, `skin_female`) VALUES ('airport', '0', 'boss', 'Air-Traffic Controller', '0', '{}', '{}'); 7 | -------------------------------------------------------------------------------- /bridge/esx/client.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | ESX = exports.es_extended:getSharedObject() 4 | 5 | function ShowNotification(text) 6 | ESX.ShowNotification(text) 7 | end 8 | 9 | function ShowHelpNotification(text) 10 | ESX.ShowHelpNotification(text) 11 | end 12 | 13 | function ServerCallback(name, cb, ...) 14 | ESX.TriggerServerCallback(name, cb, ...) 15 | end 16 | 17 | function PermissionCheck(perm) 18 | local job = ESX.GetPlayerData().job 19 | if (perm == "flight") then 20 | return (job.name == "airport") 21 | elseif (perm == "hangar") then 22 | return (job.name == "airport") 23 | elseif (perm == "pilot_mission") then 24 | return (job.name == "airport") 25 | elseif (perm == "view_tracker") then 26 | return (job.name == "airport" or job.name == "police") 27 | end 28 | end 29 | 30 | RegisterNetEvent(GetCurrentResourceName()..":showNotification", function(text) 31 | ShowNotification(text) 32 | end) -------------------------------------------------------------------------------- /bridge/qb/client.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' then return end 2 | 3 | QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | function GiveKeys(vehicle) 6 | TriggerEvent("vehiclekeys:client:SetOwner", QBCore.Functions.GetPlate(vehicle)) 7 | end 8 | 9 | function ShowNotification(text) 10 | QBCore.Functions.Notify(text) 11 | end 12 | 13 | function ShowHelpNotification(text) 14 | AddTextEntry('qbHelpNotification', text) 15 | BeginTextCommandDisplayHelp('qbHelpNotification') 16 | EndTextCommandDisplayHelp(0, false, false, -1) 17 | end 18 | 19 | function ServerCallback(name, cb, ...) 20 | QBCore.Functions.TriggerCallback(name, cb, ...) 21 | end 22 | 23 | RegisterNetEvent(GetCurrentResourceName()..":showNotification", function(text) 24 | ShowNotification(text) 25 | end) 26 | 27 | function PermissionCheck(perm) 28 | local job = QBCore.Functions.GetPlayerData().job 29 | if (perm == "flight") then 30 | return (job.name == "airport") 31 | elseif (perm == "hangar") then 32 | return (job.name == "airport") 33 | elseif (perm == "pilot_mission") then 34 | return (job.name == "airport") 35 | elseif (perm == "view_tracker") then 36 | return (job.name == "airport" or job.name == "police") 37 | end 38 | end 39 | 40 | RegisterNetEvent(GetCurrentResourceName()..":showNotification", function(text) 41 | ShowNotification(text) 42 | end) 43 | -------------------------------------------------------------------------------- /bridge/esx/server.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | ESX = exports.es_extended:getSharedObject() 4 | 5 | function RegisterCallback(name, cb) 6 | ESX.RegisterServerCallback(name, cb) 7 | end 8 | 9 | function ShowNotification(target, text) 10 | TriggerClientEvent(GetCurrentResourceName()..":showNotification", target, text) 11 | end 12 | 13 | function Search(source, name) 14 | local xPlayer = ESX.GetPlayerFromId(source) 15 | if (name ~= "money") then 16 | local item = xPlayer.getInventoryItem(name) 17 | if item ~= nil then 18 | return item.count 19 | else 20 | return 0 21 | end 22 | else 23 | return xPlayer.getMoney() 24 | end 25 | end 26 | 27 | function AddItem(source, name, amount) 28 | local xPlayer = ESX.GetPlayerFromId(source) 29 | if name == "money" then 30 | return xPlayer.addMoney(amount) 31 | else 32 | return xPlayer.addInventoryItem(name, amount) 33 | end 34 | end 35 | 36 | function RemoveItem(source, name, amount) 37 | local xPlayer = ESX.GetPlayerFromId(source) 38 | if name == "money" then 39 | return xPlayer.removeMoney(amount) 40 | else 41 | return xPlayer.removeInventoryItem(name, amount) 42 | end 43 | end 44 | 45 | function RegisterUsableItem(...) 46 | ESX.RegisterUsableItem(...) 47 | end 48 | 49 | function PermissionCheck(source, perm) 50 | local job = ESX.GetPlayerFromId(source).job 51 | if (perm == "flight") then 52 | return (job.name == "airport") 53 | elseif (perm == "pilot_mission") then 54 | return (job.name == "airport") 55 | end 56 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

More Information & Scripts can be found here!

3 | 4 | ## Preview 5 | 6 | https://www.youtube.com/watch?v=IhnLt_K1APo 7 | 8 | ## What is this? 9 | 10 |

This is a multi-framework airport system that's great for players to use as both a end-user and as a job!

11 | 12 |

It's a great fit for any type of roleplaying server, whether it's Serious RP or not.

13 | 14 | With this resource, you will be able to do the following: 15 | 16 | - Board Players as Passengers in your Aircraft. 17 | - Pick up NPC Passengers to earn money. 18 | - Pick up shipments to earn money. 19 | - Travel to other airports when other pilots aren't available. 20 | - Pay to board an aircraft through the airport terminal. 21 | 22 | ## Supported Frameworks 23 | 24 | - ESX 1.1+ & Legacy 25 | - QBCore 26 | - Standalone 27 | 28 | ## What do I need? 29 | 30 | Use a supported framework or make it work with yours via the bridge folder. 31 | 32 | Ox Lib (Required). 33 | 34 | ## Installation 35 | 36 |

Go to the "__INSTALLATION" folder.

37 |

Add the jobs from the Jobs folder into your framework.

38 | 39 | ## Need Support? 40 | 41 | Click here! 42 | 43 | ## Licensing 44 | 45 |

Do not at any point redistribute this without my permission and credit.

46 |

I've already had to issue reports on people selling my Television Script (which is free).

47 |

That is not fair to me and to the people who get scammed out of their money.

48 |

So please, respect the rules and have fun!

49 | -------------------------------------------------------------------------------- /bridge/qb/server.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' then return end 2 | 3 | QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | function RegisterCallback(name, cb) 6 | QBCore.Functions.CreateCallback(name, cb) 7 | end 8 | 9 | function ShowNotification(target, text) 10 | TriggerClientEvent(GetCurrentResourceName()..":showNotification", target, text) 11 | end 12 | 13 | function Search(source, name) 14 | local xPlayer = QBCore.Functions.GetPlayer(source) 15 | if name ~= "money" and name ~= "cash" then 16 | local item = xPlayer.Functions.GetItemByName(name) 17 | if item ~= nil then 18 | return item.amount 19 | else 20 | return 0 21 | end 22 | else 23 | return xPlayer.PlayerData.money['cash'] 24 | end 25 | end 26 | 27 | function AddItem(source, name, amount) 28 | local xPlayer = QBCore.Functions.GetPlayer(source) 29 | if name == "money" or name == "cash" then 30 | return xPlayer.Functions.AddMoney("cash", amount) 31 | else 32 | return xPlayer.Functions.AddItem(name, amount) 33 | end 34 | end 35 | 36 | function RemoveItem(source, name, amount) 37 | local xPlayer = QBCore.Functions.GetPlayer(source) 38 | if name == "money" or name == "cash" then 39 | return xPlayer.Functions.RemoveMoney("cash", amount) 40 | else 41 | return xPlayer.Functions.RemoveItem(name, amount) 42 | end 43 | end 44 | 45 | function RegisterUsableItem(...) 46 | QBCore.Functions.CreateUseableItem(...) 47 | end 48 | 49 | function PermissionCheck(source, perm) 50 | local job = QBCore.Functions.GetPlayer(source).PlayerData.job 51 | if (perm == "flight") then 52 | return (job.name == "airport") 53 | elseif (perm == "pilot_mission") then 54 | return (job.name == "airport") 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /modules/airports/server.lua: -------------------------------------------------------------------------------- 1 | local Flights = {} 2 | GlobalState.Flights = Flights 3 | 4 | function UpdateFlights() 5 | GlobalState.Flights = Flights 6 | end 7 | 8 | RegisterCallback("pickle_airport:startFlight", function(source, cb, index, flight_info) 9 | if (Flights[index]) then 10 | cb(false) 11 | return 12 | else 13 | if PermissionCheck(source, "flight") then 14 | Flights[index] = flight_info 15 | Flights[index].source = source 16 | UpdateFlights() 17 | cb(Flights[index]) 18 | SetTimeout(1000 * Config.AirportSettings.DepartureTime, function() 19 | Flights[index] = nil 20 | UpdateFlights() 21 | end) 22 | else 23 | cb(false) 24 | return 25 | end 26 | end 27 | end) 28 | 29 | RegisterCallback("pickle_airport:startNPCFlight", function(source, cb, index) 30 | if (Config.AirportSettings.NPCFlightCost and Search(source, "money") - Config.AirportSettings.NPCFlightCost >= 0) then 31 | RemoveItem(source, "money", Config.AirportSettings.NPCFlightCost) 32 | cb(true) 33 | return 34 | else 35 | cb(false) 36 | return 37 | end 38 | end) 39 | 40 | RegisterCallback("pickle_airport:purchaseTicket", function(source, cb, index) 41 | if (Flights[index] and Search(source, "money") - Flights[index].price >= 0) then 42 | RemoveItem(source, "money", Flights[index].price) 43 | cb(true) 44 | return 45 | else 46 | cb(false) 47 | return 48 | end 49 | end) 50 | 51 | 52 | RegisterNetEvent("pickle_airport:returnPassengers", function(index, passengers) 53 | local source = source 54 | if PermissionCheck(source, "flight") then 55 | for i=1, #passengers do 56 | TriggerClientEvent("pickle_airport:returnBoarding", passengers[i], index) 57 | end 58 | end 59 | end) -------------------------------------------------------------------------------- /modules/tracker/client.lua: -------------------------------------------------------------------------------- 1 | TrackedAircraft = {} 2 | 3 | function GetCurrentAircraft() 4 | local ped = PlayerPedId() 5 | local vehicle = GetVehiclePedIsIn(ped, false) 6 | if (vehicle and GetPedInVehicleSeat(vehicle, -1) == ped) then 7 | if (IsPedInAnyPlane(ped)) then 8 | return vehicle, 1 9 | elseif (IsPedInAnyHeli(ped)) then 10 | return vehicle, 2 11 | end 12 | end 13 | end 14 | 15 | function UpdateClientTracker(source, data) 16 | if (TrackedAircraft[source] and TrackedAircraft[source].blip) then 17 | RemoveBlip(TrackedAircraft[source].blip) 18 | end 19 | if (data) then 20 | TrackedAircraft[source] = data 21 | 22 | local info = Config.Tracker.BlipTypes[data.aircraftType] 23 | 24 | if (PermissionCheck("view_tracker")) then 25 | TrackedAircraft[source].blip = CreateBlip({ 26 | Location = data.coords, 27 | Rotation = data.heading, 28 | Label = info.Label, 29 | ID = info.ID, 30 | Scale = info.Scale, 31 | Color = info.Color 32 | }) 33 | end 34 | else 35 | TrackedAircraft[source] = nil 36 | end 37 | end 38 | 39 | CreateThread(function() 40 | while true do 41 | local wait = 1000 * Config.Tracker.UpdateTime 42 | local vehicle, aircraftType = GetCurrentAircraft() 43 | if vehicle then 44 | local coords = GetEntityCoords(vehicle) 45 | local heading = GetEntityHeading(vehicle) 46 | TriggerServerEvent("pickle_airport:tracker:updateAircraft", {coords = coords, heading = heading, aircraftType = aircraftType}) 47 | elseif TrackedAircraft[GetPlayerServerId(PlayerId())] then 48 | TriggerServerEvent("pickle_airport:tracker:updateAircraft", nil) 49 | end 50 | Wait(wait) 51 | end 52 | end) 53 | 54 | RegisterNetEvent("pickle_airport:tracker:receiveAircraftUpdate", function(source, data) 55 | UpdateClientTracker(source, data) 56 | end) -------------------------------------------------------------------------------- /core/client.lua: -------------------------------------------------------------------------------- 1 | function CreateVeh(modelHash, ...) 2 | RequestModel(modelHash) 3 | while not HasModelLoaded(modelHash) do Wait(0) end 4 | local veh = CreateVehicle(modelHash, ...) 5 | SetModelAsNoLongerNeeded(modelHash) 6 | if (GiveKeys) then 7 | GiveKeys(veh) 8 | end 9 | return veh 10 | end 11 | 12 | function CreateNPC(modelHash, ...) 13 | RequestModel(modelHash) 14 | while not HasModelLoaded(modelHash) do Wait(0) end 15 | local ped = CreatePed(26, modelHash, ...) 16 | SetModelAsNoLongerNeeded(modelHash) 17 | return ped 18 | end 19 | 20 | function CreateProp(modelHash, ...) 21 | RequestModel(modelHash) 22 | while not HasModelLoaded(modelHash) do Wait(0) end 23 | local obj = CreateObject(modelHash, ...) 24 | SetModelAsNoLongerNeeded(modelHash) 25 | return obj 26 | end 27 | 28 | function PlayAnim(ped, dict, ...) 29 | RequestAnimDict(dict) 30 | while not HasAnimDictLoaded(dict) do Wait(0) end 31 | TaskPlayAnim(ped, dict, ...) 32 | end 33 | 34 | function PlayEffect(dict, particleName, entity, off, rot, time, cb) 35 | CreateThread(function() 36 | RequestNamedPtfxAsset(dict) 37 | while not HasNamedPtfxAssetLoaded(dict) do 38 | Wait(0) 39 | end 40 | UseParticleFxAssetNextCall(dict) 41 | Wait(10) 42 | local particleHandle = StartParticleFxLoopedOnEntity(particleName, entity, off.x, off.y, off.z, rot.x, rot.y, rot.z, 1.0) 43 | SetParticleFxLoopedColour(particleHandle, 0, 255, 0 , 0) 44 | Wait(time) 45 | StopParticleFxLooped(particleHandle, false) 46 | cb() 47 | end) 48 | end 49 | 50 | function CreateBlip(data) 51 | local x,y,z = table.unpack(data.Location) 52 | local blip = AddBlipForCoord(x, y, z) 53 | SetBlipSprite(blip, data.ID) 54 | SetBlipDisplay(blip, 4) 55 | SetBlipScale(blip, data.Scale) 56 | SetBlipColour(blip, data.Color) 57 | if (data.Rotation) then 58 | SetBlipRotation(blip, math.ceil(data.Rotation)) 59 | end 60 | SetBlipAsShortRange(blip, true) 61 | BeginTextCommandSetBlipName("STRING") 62 | AddTextComponentString(data.Label) 63 | EndTextCommandSetBlipName(blip) 64 | return blip 65 | end 66 | 67 | for i=1, #Config.Blips do 68 | CreateBlip(Config.Blips[i]) 69 | end 70 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.Debug = true 4 | 5 | Config.MissionCommand = true 6 | 7 | Config.Blips = {} 8 | 9 | Config.Tracker = { 10 | UpdateTime = 10, 11 | BlipTypes = { 12 | { -- Plane 13 | Label = "Plane", 14 | ID = 423, 15 | Color = 38, 16 | Scale = 0.75, 17 | }, 18 | { -- Helicopter 19 | Label = "Helicopter", 20 | ID = 64, 21 | Color = 38, 22 | Scale = 0.75, 23 | }, 24 | } 25 | } 26 | 27 | Config.Missions = { 28 | Blips = { 29 | PackagePickup = { 30 | Label = "Pickup Package", 31 | ID = 569, 32 | Color = 5, 33 | Scale = 0.75 34 | }, 35 | PackageDropoff = { 36 | Label = "Drop-off Package", 37 | ID = 569, 38 | Color = 5, 39 | Scale = 0.75 40 | }, 41 | PassengerPickup = { 42 | Label = "Pickup Passengers", 43 | ID = 569, 44 | Color = 5, 45 | Scale = 0.75 46 | }, 47 | PassengerDropoff = { 48 | Label = "Drop-off Passengers", 49 | ID = 569, 50 | Color = 5, 51 | Scale = 0.75 52 | }, 53 | }, 54 | Sequences = { 55 | { 56 | Type = "Passenger", 57 | PassengerPickup = vector3(-1215.0138, -2647.5029, 14.5475), 58 | PassengerDropoff = vector3(1713.7322, 3253.1628, 41.6815), 59 | Rewards = { 60 | {name = "money", min = 300, max = 500} 61 | } 62 | }, 63 | { 64 | Type = "Passenger", 65 | PassengerPickup = vector3(1713.7322, 3253.1628, 41.6815), 66 | PassengerDropoff = vector3(-1215.0138, -2647.5029, 14.5475), 67 | Rewards = { 68 | {name = "money", min = 300, max = 500} 69 | } 70 | }, 71 | { 72 | Type = "Passenger", 73 | PassengerPickup = vector3(-1215.0138, -2647.5029, 14.5475), 74 | PassengerDropoff = vector3(2124.8884, 4804.3428, 41.7431), 75 | Rewards = { 76 | {name = "money", min = 300, max = 500} 77 | } 78 | }, 79 | { 80 | Type = "Passenger", 81 | PassengerPickup = vector3(2124.8884, 4804.3428, 41.7431), 82 | PassengerDropoff = vector3(-1215.0138, -2647.5029, 14.5475), 83 | Rewards = { 84 | {name = "money", min = 300, max = 500} 85 | } 86 | }, 87 | { 88 | Type = "Delivery", 89 | PackagePickup = vector3(-940.9973, -2954.2285, 13.9450), 90 | PackageDropoff = vector3(1720.2944, 3306.6436, 41.2235), 91 | Rewards = { 92 | {name = "money", min = 300, max = 500} 93 | } 94 | }, 95 | { 96 | Type = "Delivery", 97 | PackagePickup = vector3(1720.2944, 3306.6436, 41.2235), 98 | PackageDropoff = vector3(-940.9973, -2954.2285, 13.9450), 99 | Rewards = { 100 | {name = "money", min = 300, max = 500} 101 | } 102 | }, 103 | { 104 | Type = "Delivery", 105 | PackagePickup = vector3(-940.9973, -2954.2285, 13.9450), 106 | PackageDropoff = vector3(2137.6079, 4791.4370, 40.9703), 107 | Rewards = { 108 | {name = "money", min = 300, max = 500} 109 | } 110 | }, 111 | { 112 | Type = "Delivery", 113 | PackagePickup = vector3(2137.6079, 4791.4370, 40.9703), 114 | PackageDropoff = vector3(-940.9973, -2954.2285, 13.9450), 115 | Rewards = { 116 | {name = "money", min = 300, max = 500} 117 | } 118 | }, 119 | } 120 | } 121 | 122 | Config.MissionTypes = { 123 | Passenger = { 124 | Model = `shamal` 125 | }, 126 | Delivery = { 127 | Model = `titan` 128 | } 129 | } 130 | 131 | Config.Airports = { 132 | { 133 | AirportTitle = "LSIA Airport", 134 | Blips = { 135 | Boarding = { 136 | Label = "Airport", 137 | ID = 423, 138 | Color = 5, 139 | Scale = 0.75 140 | } 141 | }, 142 | Locations = { 143 | Boarding = vector3(-1042.4271, -2745.5457, 21.3594), 144 | Flight = vector3(-1287.2458, -2599.4785, 14.5466), 145 | Hangar = vector4(-1058.8749, -2963.2598, 13.9645, 147.2194), 146 | }, 147 | }, 148 | { 149 | AirportTitle = "Sandy Airfield", 150 | Blips = { 151 | Boarding = { 152 | Label = "Airport", 153 | ID = 423, 154 | Color = 5, 155 | Scale = 0.75 156 | } 157 | }, 158 | Locations = { 159 | Boarding = vector3(1768.0939, 3296.6040, 41.1809), 160 | Flight = vector3(1740.0123, 3282.0764, 41.0884), 161 | Hangar = vector4(1766.9825, 3245.4485, 41.8806, 14.1263), 162 | }, 163 | }, 164 | { 165 | AirportTitle = "Grapeseed Airfield", 166 | Blips = { 167 | Boarding = { 168 | Label = "Airport", 169 | ID = 423, 170 | Color = 5, 171 | Scale = 0.75 172 | } 173 | }, 174 | Locations = { 175 | Boarding = vector3(2159.0381, 4782.0737, 41.9610), 176 | Flight = vector3(2140.8184, 4815.4282, 41.2161), 177 | Hangar = vector4(2149.6863, 4808.7354, 41.1853, 107.2027), 178 | }, 179 | }, 180 | } 181 | 182 | Config.AirportSettings = { 183 | DepartureTime = 20, 184 | NPCFlightCost = 6500, 185 | } 186 | 187 | Config.Spawner = { 188 | Vehicles = { 189 | {label = "Luxor", description = "", model = `luxor`}, 190 | {label = "Shamal", description = "", model = `shamal`}, 191 | {label = "Cuban 800", description = "", model = `cuban800`}, 192 | {label = "Jet", description = "", model = `jet`}, 193 | {label = "Maverick", description = "", model = `maverick`}, 194 | {label = "Super Volito", description = "", model = `supervolito`}, 195 | } 196 | } 197 | 198 | U = {} 199 | 200 | U.permission_denied = "You can't do this." 201 | 202 | -- Delivery Mission 203 | 204 | U.package_pickup_notify = "Go to the pickup location." 205 | U.package_pickup = "Press ~INPUT_CONTEXT~ to pick up the package." 206 | U.package_load_notify = "Bring the package into the aircraft." 207 | U.package_flight_notify = "Fly the aircraft to the desination." 208 | U.package_dropoff_notify = "Go to the drop-off location." 209 | U.package_dropoff = "Press ~INPUT_CONTEXT~ to drop-off package." 210 | 211 | -- Passenger Mission 212 | 213 | U.passenger_pickup_notify = "Go to the flight terminal." 214 | U.passenger_pickup = "Press ~INPUT_CONTEXT~ to pick up the passengers." 215 | U.passenger_loading = "Loading Passengers..." 216 | U.passenger_unloading = "Unloading Passengers..." 217 | U.passenger_dropoff_notify = "Fly the aircraft to the desination." 218 | U.passenger_dropoff = "Press ~INPUT_CONTEXT~ to drop-off the passengers." 219 | 220 | -- Airport 221 | U.Boarding_interact = "Press ~INPUT_CONTEXT~ to board a flight." 222 | U.Boarding_npc_interact = "Press ~INPUT_CONTEXT~ to fly for: $" 223 | U.Boarding_unavailable = "There are no flights available." 224 | U.Boarding_broke = "You can't afford to purchase this plane ticket." 225 | U.Boarding_full = "You can't join the flight as it is full." 226 | 227 | U.Flight_interact = "Press ~INPUT_CONTEXT~ to start a flight." 228 | U.Flight_return = "Press ~INPUT_CONTEXT~ to drop-off your passengers." 229 | U.Flight_reject = "You need to be in a plane to start a flight." 230 | U.Flight_denied = "You can't do this." 231 | 232 | U.Hangar_interact = "Press ~INPUT_CONTEXT~ to retreive an aircraft." 233 | U.Hangar_return = "Press ~INPUT_CONTEXT~ to return your aircraft." 234 | U.Hangar_denied = "You can't do this." -------------------------------------------------------------------------------- /modules/missions/client.lua: -------------------------------------------------------------------------------- 1 | local MissionIndex, MissionAircraft, MissionBlip = nil, nil, nil 2 | 3 | function SetMissionBlip(coords, blipType) 4 | if MissionBlip ~= nil then 5 | RemoveBlip(MissionBlip) 6 | MissionBlip = nil 7 | end 8 | if coords then 9 | local info = Config.Missions.Blips[blipType] 10 | MissionBlip = CreateBlip({ 11 | Location = coords, 12 | Label = info.Label, 13 | ID = info.ID, 14 | Scale = info.Scale, 15 | Color = info.Color 16 | }) 17 | end 18 | end 19 | 20 | function DeliveryMission(index, cb) 21 | local mission = Config.Missions.Sequences[index] 22 | local boxProp = nil 23 | 24 | local function Cleanup() 25 | SetMissionBlip() 26 | 27 | if boxProp then 28 | local ped = PlayerPedId() 29 | DeleteEntity(boxProp) 30 | ClearPedSecondaryTask(ped) 31 | boxProp = nil 32 | end 33 | end 34 | 35 | Citizen.CreateThread(function() 36 | local done = false 37 | 38 | ShowNotification(U.package_pickup_notify) 39 | SetMissionBlip(mission.PackagePickup, "PackagePickup") 40 | 41 | while index == MissionIndex and not done do 42 | if (not DoesEntityExist(MissionAircraft) or GetEntityHealth(MissionAircraft) <= 0) then 43 | Cleanup() 44 | cb(false) 45 | return 46 | end 47 | local wait = 1000 48 | local ped = PlayerPedId() 49 | local coords = mission.PackagePickup 50 | local pcoords = GetEntityCoords(ped) 51 | local dist = #(coords - pcoords) 52 | if (dist < 20) then 53 | wait = 0 54 | DrawMarker(2, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.25, 0.25, 255, 255, 255, 127, false, true) 55 | if (dist < 1.25 and not ShowHelpNotification(U.package_pickup) and IsControlJustPressed(1, 51)) then 56 | done = true 57 | break 58 | end 59 | end 60 | Wait(wait) 61 | end 62 | 63 | if (not done) then 64 | Cleanup() 65 | cb(false) 66 | return 67 | end 68 | done = false 69 | 70 | ShowNotification(U.package_dropoff_notify) 71 | SetMissionBlip(mission.PackageDropoff, "PackageDropoff") 72 | 73 | while index == MissionIndex and not done do 74 | if (not DoesEntityExist(MissionAircraft) or GetEntityHealth(MissionAircraft) <= 0) then 75 | Cleanup() 76 | cb(false) 77 | return 78 | end 79 | 80 | local wait = 1000 81 | local ped = PlayerPedId() 82 | local coords = mission.PackageDropoff 83 | local pcoords = GetEntityCoords(ped) 84 | local acoords = GetEntityCoords(MissionAircraft) 85 | local dist = #(coords - pcoords) 86 | local adist = #(coords - acoords) 87 | local vehicle = GetVehiclePedIsIn(ped) 88 | if (vehicle ~= 0 and boxProp) then 89 | DeleteEntity(boxProp) 90 | ClearPedSecondaryTask(ped) 91 | boxProp = nil 92 | elseif (vehicle == 0) then 93 | if not IsEntityPlayingAnim(ped, "anim@heists@box_carry@", "idle", 13) then 94 | PlayAnim(ped, "anim@heists@box_carry@", "idle", -8.0, 8.0, -1, 49, 1.0) 95 | Wait(10) 96 | end 97 | if (not boxProp) then 98 | local bone = GetPedBoneIndex(ped, 60309) 99 | local c, r = vec3(0.025, 0.08, 0.285), vec3(-165.0, 250.0, 0.0) 100 | boxProp = CreateProp(`hei_prop_heist_box`, pcoords.x, pcoords.y, pcoords.z, true, true) 101 | AttachEntityToEntity(boxProp, ped, bone, c.x, c.y, c.z, r.x, r.y, r.z, false, false, false, false, 2, true) 102 | elseif (boxProp and dist < 20 and adist < 40) then 103 | wait = 0 104 | DrawMarker(2, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.25, 0.25, 255, 255, 255, 127, false, true) 105 | if (dist < 1.25 and not ShowHelpNotification(U.package_dropoff) and IsControlJustPressed(1, 51)) then 106 | done = true 107 | break 108 | end 109 | end 110 | end 111 | Wait(wait) 112 | end 113 | 114 | Cleanup() 115 | 116 | cb(done) 117 | end) 118 | end 119 | 120 | function PassengerMission(index, cb) 121 | local mission = Config.Missions.Sequences[index] 122 | local peds = {} 123 | local seats = GetVehicleModelNumberOfSeats(GetEntityModel(MissionAircraft)) - 2 124 | 125 | local function Cleanup() 126 | SetMissionBlip() 127 | for i=1, #peds do 128 | DeleteEntity(peds[i]) 129 | end 130 | end 131 | Citizen.CreateThread(function() 132 | local done = false 133 | 134 | ShowNotification(U.passenger_pickup_notify) 135 | SetMissionBlip(mission.PassengerPickup, "PassengerPickup") 136 | 137 | while index == MissionIndex and not done do 138 | if (not DoesEntityExist(MissionAircraft) or GetEntityHealth(MissionAircraft) <= 0) then 139 | Cleanup() 140 | cb(false) 141 | return 142 | end 143 | local wait = 1000 144 | local ped = PlayerPedId() 145 | local coords = mission.PassengerPickup 146 | local pcoords = GetEntityCoords(ped) 147 | local dist = #(coords - pcoords) 148 | if (dist < 20) then 149 | wait = 0 150 | DrawMarker(2, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75, 0.75, 0.75, 255, 255, 255, 127, false, true) 151 | if (dist < 1.25 and not ShowHelpNotification(U.passenger_pickup) and IsControlJustPressed(1, 51)) then 152 | done = true 153 | break 154 | end 155 | end 156 | Wait(wait) 157 | end 158 | 159 | if (done) then 160 | ShowNotification(U.passenger_loading) 161 | FreezeEntityPosition(MissionAircraft, true) 162 | local vehicle = MissionAircraft 163 | local coords = mission.PassengerPickup 164 | for i=1, seats do 165 | if IsVehicleSeatFree(vehicle, i) then 166 | local ped = CreateNPC(`g_m_m_armboss_01`, coords.x, coords.y, coords.z + 100.0, 0.0, true, true) 167 | peds[#peds + 1] = ped 168 | TaskWarpPedIntoVehicle(ped, vehicle, i) 169 | Wait(1000) 170 | end 171 | end 172 | FreezeEntityPosition(MissionAircraft, false) 173 | else 174 | Cleanup() 175 | cb(false) 176 | return 177 | end 178 | 179 | done = false 180 | 181 | ShowNotification(U.passenger_dropoff_notify) 182 | SetMissionBlip(mission.PassengerDropoff, "PassengerDropoff") 183 | 184 | while index == MissionIndex and not done do 185 | if (not DoesEntityExist(MissionAircraft) or GetEntityHealth(MissionAircraft) <= 0) then 186 | Cleanup() 187 | cb(false) 188 | return 189 | end 190 | 191 | local wait = 1000 192 | local ped = PlayerPedId() 193 | local coords = mission.PassengerDropoff 194 | local pcoords = GetEntityCoords(ped) 195 | local acoords = GetEntityCoords(MissionAircraft) 196 | local dist = #(coords - pcoords) 197 | local adist = #(coords - acoords) 198 | local vehicle = GetVehiclePedIsIn(ped) 199 | if (vehicle == MissionAircraft and dist < 20 and adist < 40) then 200 | wait = 0 201 | DrawMarker(2, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75, 0.75, 0.75, 255, 255, 255, 127, false, true) 202 | if (dist < 1.25 and not ShowHelpNotification(U.passenger_dropoff) and IsControlJustPressed(1, 51)) then 203 | done = true 204 | break 205 | end 206 | end 207 | Wait(wait) 208 | end 209 | 210 | if (done) then 211 | ShowNotification(U.passenger_unloading) 212 | FreezeEntityPosition(MissionAircraft, true) 213 | local vehicle = MissionAircraft 214 | for i=1, #peds do 215 | DeleteEntity(peds[i]) 216 | Wait(1000) 217 | end 218 | FreezeEntityPosition(MissionAircraft, false) 219 | Cleanup() 220 | else 221 | Cleanup() 222 | cb(false) 223 | return 224 | end 225 | 226 | 227 | cb(done) 228 | end) 229 | end 230 | 231 | function StopMission() 232 | MissionIndex = nil 233 | MissionAircraft = nil 234 | SetMissionBlip() 235 | end 236 | 237 | function StartMission(Type) 238 | local vehicle = (Config.MissionCommand and GetCurrentAircraft() or nil) 239 | if MissionIndex == nil then 240 | if not vehicle then 241 | if (Type and Config.MissionTypes[Type]) then 242 | local model = Config.MissionTypes[Type].Model 243 | local coords, heading = GetEntityCoords(PlayerPedId()), GetEntityHeading(PlayerPedId()) 244 | vehicle = CreateVeh(model, coords.x, coords.y, coords.z, heading, true, true) 245 | TaskWarpPedIntoVehicle(PlayerPedId(), vehicle, -1) 246 | Wait(100) 247 | else 248 | ShowNotification("You cannot start a mission without being in a aircraft.") 249 | return 250 | end 251 | end 252 | local index = GetRandomInt(1, #Config.Missions.Sequences) 253 | if Type then 254 | for i=1, 10 do 255 | if Config.Missions.Sequences[index].Type ~= Type then 256 | index = GetRandomInt(1, #Config.Missions.Sequences) 257 | else 258 | break 259 | end 260 | end 261 | if Config.Missions.Sequences[index].Type ~= Type then 262 | ShowNotification("There are no missions for this category.") 263 | return 264 | end 265 | end 266 | MissionIndex = index 267 | MissionAircraft = vehicle 268 | local mission = Config.Missions.Sequences[MissionIndex] 269 | if (mission.Type == "Delivery") then 270 | DeliveryMission(MissionIndex, function(result) 271 | if result then 272 | ShowNotification("Mission completed!") 273 | TriggerServerEvent("pickle_airport:finishedMission", MissionIndex) 274 | else 275 | ShowNotification("Mission failed.") 276 | end 277 | StopMission() 278 | end) 279 | elseif (mission.Type == "Passenger") then 280 | PassengerMission(MissionIndex, function(result) 281 | if result then 282 | ShowNotification("Mission completed!") 283 | TriggerServerEvent("pickle_airport:finishedMission", MissionIndex) 284 | else 285 | ShowNotification("Mission failed.") 286 | end 287 | StopMission() 288 | end) 289 | end 290 | else 291 | StopMission() 292 | end 293 | end 294 | 295 | function OpenMissionMenu(index) 296 | local menu_id = "airport_mission_menu" 297 | local options = { 298 | {label = "Passenger Flights", description = ""}, 299 | {label = "Package Delivery", description = ""}, 300 | } 301 | lib.registerMenu({ 302 | id = menu_id, 303 | title = 'Aircraft Spawner', 304 | position = 'top-left', 305 | onClose = function(keyPressed) 306 | Interact = false 307 | end, 308 | options = options 309 | }, function(selected, scrollIndex, args) 310 | Interact = false 311 | if (selected == 1) then 312 | StartMission("Passenger") 313 | elseif (selected == 2) then 314 | StartMission("Delivery") 315 | end 316 | end) 317 | 318 | lib.showMenu(menu_id) 319 | end 320 | 321 | if Config.MissionCommand then 322 | RegisterCommand("pilotmission", function() 323 | if (PermissionCheck("pilot_mission")) then 324 | StartMission() 325 | else 326 | ShowNotification(U.permission_denied) 327 | end 328 | end) 329 | end 330 | 331 | -------------------------------------------------------------------------------- /modules/airports/client.lua: -------------------------------------------------------------------------------- 1 | Interact = false 2 | 3 | function StartNPCFlight(from, index) 4 | ServerCallback("pickle_airport:startNPCFlight", function(result) 5 | if (result) then 6 | local Plane = nil 7 | local airport_cfg = Config.Airports[from] 8 | local cfg = Config.Airports[index] 9 | local coords = cfg.Locations.Boarding 10 | local ped = PlayerPedId() 11 | DoScreenFadeOut(1000) 12 | Wait(1400) 13 | local data = Flight 14 | local start, finish, board = airport_cfg.Locations.Flight, cfg.Locations.Flight, cfg.Locations.Boarding 15 | 16 | start, finish = vec3(start.x, start.y, 1700.0), vec3(finish.x, finish.y, 1700.0) 17 | 18 | local heading = GetHeadingFromVector_2d(start.x - finish.x, start.y - finish.y) 19 | local ped = CreateNPC(`g_m_m_armboss_01`, start.x, start.y, start.z, 0.0, true, true) 20 | 21 | Plane = CreateVeh(`luxor`, start.x, start.y, start.z, heading - 180.0, false, true) 22 | SetEntityCoords(ped, start.x, start.y, start.z) 23 | TaskWarpPedIntoVehicle(ped, Plane, -1) 24 | TaskWarpPedIntoVehicle(PlayerPedId(), Plane, 1) 25 | 26 | Wait(100) 27 | DoScreenFadeIn(1000) 28 | 29 | local percent = 0 30 | while percent < 1.0 do 31 | percent = percent + 0.0005 32 | local coords = lerp(start, finish, percent) 33 | SetEntityCoords(Plane, coords.x, coords.y, coords.z) 34 | DisableControlAction(1, 75, 1) 35 | Wait(0) 36 | end 37 | FreezeEntityPosition(Plane, true) 38 | DoScreenFadeOut(1000) 39 | Wait(1500) 40 | DeleteEntity(Plane) 41 | SetEntityCoords(PlayerPedId(), board.x, board.y, board.z) 42 | 43 | Wait(100) 44 | DoScreenFadeIn(1000) 45 | else 46 | ShowNotification(U.Boarding_broke) 47 | end 48 | Interact = false 49 | end, index) 50 | end 51 | 52 | function JoinActiveFlight(index) 53 | local airport = Config.Airports[index] 54 | if GlobalState.Flights[index] then 55 | local data = GlobalState.Flights[index] 56 | CreateThread(function() 57 | local ped = PlayerPedId() 58 | DoScreenFadeOut(1000) 59 | Wait(1500) 60 | SetEntityCoords(ped, data.coords.x, data.coords.y, data.coords.z + 10.0) 61 | Wait(100) 62 | local vehicle = NetToVeh(data.net_id) 63 | local seats = GetVehicleModelNumberOfSeats(GetEntityModel(vehicle)) - 2 64 | local found = false 65 | for i=1, seats do 66 | if IsVehicleSeatFree(vehicle, i) then 67 | TaskWarpPedIntoVehicle(ped, vehicle, i) 68 | found = true 69 | break 70 | end 71 | end 72 | if not found then 73 | local coords = airport.Locations.Boarding 74 | SetEntityCoords(ped, coords.x, coords.y, coords.z - 1.0) 75 | Wait(100) 76 | DoScreenFadeIn(1000) 77 | ShowNotification(U.Boarding_full) 78 | else 79 | ServerCallback("pickle_airport:purchaseTicket", function(result) 80 | if (not result) then 81 | local coords = airport.Locations.Boarding 82 | SetEntityCoords(ped, coords.x, coords.y, coords.z - 1.0) 83 | Wait(100) 84 | ShowNotification(U.Boarding_broke) 85 | end 86 | DoScreenFadeIn(1000) 87 | end, index) 88 | end 89 | Interact = false 90 | end) 91 | else 92 | Interact = false 93 | end 94 | end 95 | 96 | function OpenFlightMenu(index, vehicle) 97 | local menu_id = "airport_flight_menu" 98 | local options = {} 99 | for i=1, #Config.Airports do 100 | if index ~= i then 101 | local _index = i 102 | options[#options + 1] = {label = Config.Airports[i].AirportTitle, description = "", index = _index} 103 | end 104 | end 105 | lib.registerMenu({ 106 | id = menu_id, 107 | title = 'Choose Destination', 108 | position = 'top-left', 109 | onClose = function(keyPressed) 110 | Interact = false 111 | end, 112 | options = options 113 | }, function(selected, scrollIndex, args) 114 | Wait(250) 115 | local input = lib.inputDialog('Flight Cost', {'Price'}) 116 | if not input or tonumber(input[1]) == nil then 117 | Interact = false 118 | return 119 | else 120 | local flight_info = {destination = options[selected].index, price = math.ceil(tonumber(input[1])), coords = GetEntityCoords(vehicle), net_id = VehToNet(vehicle)} 121 | ServerCallback("pickle_airport:startFlight", function(result) 122 | if (result) then 123 | ShowNotification("Boarding Players, Takeoff Time: ".. Config.AirportSettings.DepartureTime .. " (s)") 124 | FreezeEntityPosition(vehicle, true) 125 | Wait(1000 * Config.AirportSettings.DepartureTime) 126 | FreezeEntityPosition(vehicle, false) 127 | ShowNotification("Players are now boarded, you are clear for takeoff.") 128 | end 129 | Interact = false 130 | end, index, flight_info) 131 | end 132 | end) 133 | lib.showMenu(menu_id) 134 | end 135 | 136 | function OpenSpawnMenu(index) 137 | local menu_id = "airport_spawn_menu" 138 | local options = Config.Spawner.Vehicles 139 | local cfg = Config.Airports[index] 140 | local coords = cfg.Locations.Hangar 141 | lib.registerMenu({ 142 | id = menu_id, 143 | title = 'Aircraft Spawner', 144 | position = 'top-left', 145 | onClose = function(keyPressed) 146 | Interact = false 147 | end, 148 | options = options 149 | }, function(selected, scrollIndex, args) 150 | local data = options[selected] 151 | local vehicle = CreateVeh(data.model, coords.x, coords.y, coords.z, coords.w, true, true) 152 | TaskWarpPedIntoVehicle(PlayerPedId(), vehicle, -1) 153 | Interact = false 154 | end) 155 | 156 | lib.showMenu(menu_id) 157 | end 158 | 159 | function ShowHangarMenu(index) 160 | local menu_id = "airport_hangar_menu" 161 | local options = { 162 | {label = "Aircraft Spawner", description = ""}, 163 | {label = "Mission Selector", description = ""}, 164 | } 165 | lib.registerMenu({ 166 | id = menu_id, 167 | title = 'Hangar', 168 | position = 'top-left', 169 | onClose = function(keyPressed) 170 | Interact = false 171 | end, 172 | options = options 173 | }, function(selected, scrollIndex, args) 174 | if (selected == 1) then 175 | OpenSpawnMenu(index) 176 | elseif (selected == 2) then 177 | OpenMissionMenu(index) 178 | end 179 | end) 180 | 181 | lib.showMenu(menu_id) 182 | end 183 | 184 | function OpenNPCMenu(index) 185 | local menu_id = "airport_npc_menu" 186 | local options = {} 187 | for i=1, #Config.Airports do 188 | if index ~= i then 189 | local _index = i 190 | options[#options + 1] = {label = Config.Airports[i].AirportTitle, description = "", index = _index} 191 | end 192 | end 193 | lib.registerMenu({ 194 | id = menu_id, 195 | title = 'Choose Destination', 196 | position = 'top-left', 197 | onClose = function(keyPressed) 198 | Interact = false 199 | end, 200 | options = options 201 | }, function(selected, scrollIndex, args) 202 | StartNPCFlight(index, options[selected].index) 203 | end) 204 | lib.showMenu(menu_id) 205 | end 206 | 207 | function InteractAirport(index, key) 208 | Interact = true 209 | if (key == "Boarding") then 210 | local data = GlobalState.Flights[index] 211 | if (data) then 212 | JoinActiveFlight(index) 213 | elseif (Config.AirportSettings.NPCFlightCost) then 214 | OpenNPCMenu(index) 215 | else 216 | ShowHelpNotification(U.Boarding_unavailable) 217 | end 218 | elseif (key == "Flight") then 219 | if not PermissionCheck("flight") then 220 | Interact = false 221 | ShowNotification(U.Flight_denied) 222 | return 223 | end 224 | local vehicle = GetCurrentAircraft() 225 | if vehicle then 226 | local seats = GetVehicleModelNumberOfSeats(GetEntityModel(vehicle)) - 2 227 | local players = {} 228 | local found = false 229 | for i=1, seats do 230 | if not IsVehicleSeatFree(vehicle, i) then 231 | local player = NetworkGetPlayerIndexFromPed(GetPedInVehicleSeat(vehicle, i)) 232 | if (player) then 233 | found = true 234 | players[#players + 1] = GetPlayerServerId(player) 235 | end 236 | end 237 | end 238 | if found then 239 | TriggerServerEvent("pickle_airport:returnPassengers", index, players) 240 | Interact = false 241 | else 242 | OpenFlightMenu(index, vehicle) 243 | end 244 | else 245 | Interact = false 246 | --ShowNotification("You are not able to start a flight without being in a plane.") 247 | end 248 | elseif (key == "Hangar") then 249 | if not PermissionCheck("hangar") then 250 | Interact = false 251 | ShowNotification(U.Hangar_denied) 252 | return 253 | end 254 | local vehicle = GetCurrentAircraft() 255 | if vehicle then 256 | DeleteEntity(vehicle) 257 | Interact = false 258 | else 259 | ShowHangarMenu(index) 260 | end 261 | end 262 | end 263 | 264 | function ShowInteract(index, key) 265 | local airport = Config.Airports[index] 266 | local ped = PlayerPedId() 267 | local pcoords = GetEntityCoords(ped) 268 | local coords, heading = v3(airport.Locations[key]) 269 | local dist = #(coords - pcoords) 270 | if not Interact and dist < 20.0 then 271 | if (dist < 1.75) then 272 | if (key == "Boarding") then 273 | local data = GlobalState.Flights[index] 274 | if (data) then 275 | local airport = Config.Airports[index] 276 | local dest = Config.Airports[data.destination] 277 | ShowHelpNotification("~INPUT_CONTEXT~ " .. airport.AirportTitle .. " -> " .. dest.AirportTitle .. " ($" .. data.price .. ")") 278 | elseif (Config.AirportSettings.NPCFlightCost) then 279 | ShowHelpNotification(U.Boarding_npc_interact .. Config.AirportSettings.NPCFlightCost .. ".") 280 | else 281 | ShowHelpNotification(U.Boarding_unavailable) 282 | end 283 | return true, true 284 | elseif (key == "Flight") then 285 | local vehicle = GetCurrentAircraft() 286 | if vehicle then 287 | local seats = GetVehicleModelNumberOfSeats(GetEntityModel(vehicle)) - 2 288 | local found = false 289 | for i=1, seats do 290 | if not IsVehicleSeatFree(vehicle, i) then 291 | found = true 292 | break 293 | end 294 | end 295 | if found then 296 | ShowHelpNotification(U.Flight_return) 297 | else 298 | ShowHelpNotification(U.Flight_interact) 299 | end 300 | return true, true 301 | else 302 | ShowHelpNotification(U.Flight_reject) 303 | return false, true 304 | end 305 | elseif (key == "Hangar") then 306 | local vehicle = GetCurrentAircraft() 307 | if vehicle then 308 | ShowHelpNotification(U.Hangar_return) 309 | else 310 | ShowHelpNotification(U.Hangar_interact) 311 | end 312 | return true, true 313 | end 314 | else 315 | if (key == "Boarding") then 316 | return nil, true 317 | elseif (key == "Flight") then 318 | return nil, true 319 | elseif (key == "Hangar") then 320 | return nil, true 321 | end 322 | end 323 | end 324 | end 325 | 326 | RegisterNetEvent("pickle_airport:returnBoarding", function(index) 327 | local cfg = Config.Airports[index] 328 | local coords = cfg.Locations.Flight 329 | local ped = PlayerPedId() 330 | if #(coords - GetEntityCoords(ped)) < 10.0 then 331 | local coords = cfg.Locations.Boarding 332 | DoScreenFadeOut(1000) 333 | Wait(1500) 334 | SetEntityCoords(ped, coords.x, coords.y, coords.z) 335 | Wait(100) 336 | DoScreenFadeIn(1000) 337 | end 338 | end) 339 | 340 | CreateThread(function() 341 | for i=1, #Config.Airports do 342 | local airport = Config.Airports[i] 343 | if airport.Blips then 344 | for k,v in pairs(airport.Blips) do 345 | local data = v 346 | data.Location = v3(airport.Locations[k]) 347 | CreateBlip(data) 348 | end 349 | end 350 | end 351 | while true do 352 | local wait = 1000 353 | for i=1, #Config.Airports do 354 | local airport = Config.Airports[i] 355 | for k,v in pairs(airport.Locations) do 356 | local inZone, displayZone = ShowInteract(i, k) 357 | if (displayZone) then 358 | wait = 0 359 | local coords, heading = v3(v) 360 | DrawMarker(2, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.25, 0.25, 255, 255, 255, 127, false, true) 361 | if (inZone and IsControlJustPressed(1, 51)) then 362 | InteractAirport(i, k) 363 | end 364 | end 365 | end 366 | end 367 | Wait(wait) 368 | end 369 | end) --------------------------------------------------------------------------------