diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0a0b726
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+## 1.0
+
+- Added support for NativeUILua.
+- Added support for EssentialMode.
+- Added blips for **Los Santos International Airport** and **Sandy Shores Airfield.**
+- Players can travel from **Los Santos International Airport** to **Sandy Shores Airfield.**.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..beba45d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# FiveM Airports
+[![GitHub release](https://img.shields.io/github/release/Fivem-Scripts/airports.svg)](https://github.com/FiveM-Scripts/airports/releases/latest)
+[![GitHub license](https://img.shields.io/github/license/FiveM-Scripts/airports.svg)](https://github.com/FiveM-Scripts/airports/blob/master/LICENSE)
+
+
+With this resource players can fly from **Los Santos International Airport** to **Sandy Shores Airfield** and back.
+
+## Requirements
+- [NativeUILua](https://github.com/FrazzIe/NativeUILua)
+- [Essentialmode](https://forum.fivem.net/t/release-essentialmode-base/3665) (optional)
+
+## Installation
+1. Download the latest [release](https://github.com/FiveM-Scripts/airports/releases)
+2. Extract the files to `resources/airports`
+3. Add `start airports` to your **server.cfg**
+4. (re)start your server.
+
+## Changelog
+You can find the changelog [here](https://github.com/FiveM-Scripts/airports/CHANGELOG.md).
\ No newline at end of file
diff --git a/__resource.lua b/__resource.lua
new file mode 100644
index 0000000..790af48
--- /dev/null
+++ b/__resource.lua
@@ -0,0 +1,19 @@
+resource_manifest_version '05cfa83c-a124-4cfa-a768-c24a5811d8f9'
+resource_version '1.0'
+
+dependency 'NativeUI'
+
+client_script '@NativeUI/NativeUI.lua'
+
+client_scripts {
+ 'config.lua',
+ 'client/spawn.lua',
+ 'client/menu.lua',
+ 'client/airports.lua',
+ 'client/client.lua'
+}
+
+server_script {
+ 'config.lua',
+ 'server/server.lua'
+}
\ No newline at end of file
diff --git a/client/airports.lua b/client/airports.lua
new file mode 100644
index 0000000..4016f39
--- /dev/null
+++ b/client/airports.lua
@@ -0,0 +1,61 @@
+--[[
+ FiveM Airports
+ Copyright (C) 2018 FiveM-Scripts
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this resource in the file "LICENSE". If not, see .
+]]
+
+StarPoints = {
+ {x= -1030.8077, y= -2493.35766, z=20.16929, desc="Los Santos International"},
+ {x= 1752.15, y= 3290.56, z= 41.1109, desc="Sandy shores"}
+}
+
+local function CreateAirportBlips()
+ for k,v in pairs(StarPoints) do
+ blip = AddBlipForCoord(v.x, v.y, v.z-1)
+ SetBlipSprite(blip, 90)
+ SetBlipAsShortRange(blip, true)
+
+ BeginTextCommandSetBlipName("STRING")
+ AddTextComponentString("Airport")
+ EndTextCommandSetBlipName(blip)
+ end
+end
+
+function IsPlayerNearAirport()
+ for k,v in pairs(StarPoints) do
+ if GetDistanceBetweenCoords(GetEntityCoords(PlayerPedId(), true), v.x, v.y, v.z, true) < 80.0 then
+ if not IsPedInAnyPlane(PlayerPedId()) then
+ if not _menuPool:IsAnyMenuOpen() then
+ DrawMarker(1, v.x, v.y, v.z-1.0001, 0, 0, 0, 0, 0, 0, 2.0, 2.0, 2.0, 255, 219, 77, 155, 0, 0, 2, 0, 0, 0, 0)
+ end
+ end
+ end
+
+ if GetDistanceBetweenCoords(GetEntityCoords(PlayerPedId(), true), v.x, v.y, v.z, true) < 2.0 then
+ if not _menuPool:IsAnyMenuOpen() then
+ if not IsHelpMessageBeingDisplayed() then
+ BeginTextCommandDisplayHelp("STRING")
+ AddTextComponentSubstringPlayerName("Press ~INPUT_CONTEXT~ to travel.")
+ EndTextCommandDisplayHelp(0, 0, 1, -1)
+ end
+ else
+ ClearAllHelpMessages()
+ end
+ return true
+ end
+ end
+end
+
+Citizen.CreateThread(function()
+ CreateAirportBlips()
+end)
\ No newline at end of file
diff --git a/client/client.lua b/client/client.lua
new file mode 100644
index 0000000..eccfb1d
--- /dev/null
+++ b/client/client.lua
@@ -0,0 +1,96 @@
+--[[
+ FiveM Airports
+ Copyright (C) 2018 FiveM-Scripts
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this resource in the file "LICENSE". If not, see .
+]]
+
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(0)
+ if IsPlayerNearAirport() then
+ if IsControlJustPressed(0, 38) then
+ if not IsPlayerWantedLevelGreater(PlayerId(), 0) then
+ mainMenu:Visible(not mainMenu:Visible())
+ else
+ if IsHelpMessageBeingDisplayed() then
+ ClearAllHelpMessages()
+ end
+
+ BeginTextCommandDisplayHelp("STRING")
+ AddTextComponentSubstringPlayerName("Lose the Cops")
+ EndTextCommandDisplayHelp(0, 0, 1, -1)
+ end
+ end
+ else
+ if _menuPool:IsAnyMenuOpen() then
+ mainMenu:Visible(not mainMenu:Visible())
+ end
+ end
+
+ if not landing then
+ if IsEntityInAir(AirPlane) then
+ SetVehicleLandingGear(AirPlane, 1)
+ end
+
+ if startZone == "AIRP" and planeDest == "DESRT" then
+ if IsEntityInZone(AirPlane, "DESRT") or IsEntityInZone(PlayerPedId(), "GREATC") then
+ TaskPlaneLand(pilot, AirPlane, 881.4462, 3060.4829, 41.1682+10.0001, 1657.07, 3238.21, 40.5669+1.0001)
+ SetPedKeepTask(pilot, true)
+ landing = true
+ end
+ elseif startZone == "DESRT" and planeDest == "AIRP" then
+ if IsEntityInZone(AirPlane, "RICHM") or IsEntityInZone(AirPlane, "OCEANA") then
+ TaskPlaneLand(pilot, AirPlane, -1792.00122, -2882.29980, 13.9440+1.0001, -998.5266, -3341.3579, 13.9444+1.0001)
+ SetPedKeepTask(pilot, true)
+ landing = true
+ end
+ end
+ end
+
+ if not IsEntityInZone(PlayerPedId(), startZone) then
+ if not IsEntityInAir(AirPlane) and IsPedInAnyPlane(PlayerPedId()) then
+ TaskVehicleTempAction(pilot, Airplane, 27, -1)
+ SetVehicleHandbrake(AirPlane, true)
+
+ if GetEntitySpeed(AirPlaine) == 0.0 then
+ if IsEntityInZone(PlayerPedId(), "AIRP") then
+ DoScreenFadeOut(200)
+ while not IsScreenFadedOut() do
+ Citizen.Wait(0)
+ end
+
+ SetEntityCoords(PlayerPedId(), -1042.0395, -2740.7780, 20.1692)
+ SetEntityHeading(PlayerPedId(), 340.2285)
+ Citizen.Wait(500)
+ DoScreenFadeIn(500)
+ else
+ TaskLeaveVehicle(PlayerPedId(), AirPlane, 0)
+ end
+ end
+ end
+
+ if not IsPedInVehicle(PlayerPedId(), AirPlane, false) and landing == true then
+ SetVehicleHandbrake(AirPlane, false)
+ SetBlockingOfNonTemporaryEvents(pilot, false)
+
+ SetEntityAsNoLongerNeeded(pilot)
+ SetEntityAsNoLongerNeeded(AirPlane)
+
+ startZone = nil
+ planeDest = nil
+ landing = false
+ end
+ end
+
+ end
+end)
\ No newline at end of file
diff --git a/client/menu.lua b/client/menu.lua
new file mode 100644
index 0000000..9c67f53
--- /dev/null
+++ b/client/menu.lua
@@ -0,0 +1,78 @@
+--[[
+ FiveM Airports
+ Copyright (C) 2018 FiveM-Scripts
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this resource in the file "LICENSE". If not, see .
+]]
+
+_menuPool = NativeUI.CreatePool()
+mainMenu = NativeUI.CreateMenu("", "~s~DESTINATION", "", "", "shopui_title_exec_vechupgrade", "shopui_title_exec_vechupgrade")
+_menuPool:Add(mainMenu)
+
+_menuPool:ControlDisablingEnabled(false)
+_menuPool:MouseControlsEnabled(false)
+
+function ShowNotification(text)
+ SetNotificationTextEntry("STRING")
+ AddTextComponentSubstringPlayerName(text)
+ DrawNotification(false, false)
+end
+
+function AddAirPortMenu(menu)
+ if config.use_essentialmode then
+ SantonsButton = NativeUI.CreateItem("Los Santos International Airport", "Buy a ticket for "..config.moneyCurrency .." " ..config.ticketPrice)
+ DesrtButton = NativeUI.CreateItem("Sandy Shores Airfield", "Buy a ticket for "..config.moneyCurrency .." " ..config.ticketPrice)
+ else
+ SantonsButton = NativeUI.CreateItem("Los Santos International Airport", "")
+ DesrtButton = NativeUI.CreateItem("Sandy Shores Airfield", "")
+ end
+
+ menu:AddItem(SantonsButton)
+ menu:AddItem(DesrtButton)
+ menu.OnItemSelect = function(sender, item, index)
+ if item == DesrtButton then
+ if not IsEntityInZone(PlayerPedId(), "DESRT") then
+ startZone = "AIRP"
+ planeDest = "DESRT"
+ if config.use_essentialmode then
+ TriggerServerEvent('airports:payTicket', -1675.2446, -2798.8835, 14.5409, 327.8560, planeDest, config.ticketPrice)
+ else
+ CreatePlane(-1675.2446, -2798.8835, 14.5409, 327.8560, planeDest)
+ end
+ else
+ ShowNotification("No plane is ~y~scheduled~w~ to that location right now.")
+ end
+ elseif item == SantonsButton then
+ if not IsEntityInZone(PlayerPedId(), "AIRP") then
+ startZone = "DESRT"
+ planeDest = "AIRP"
+ if config.use_essentialmode then
+ TriggerServerEvent('airports:payTicket', 1599.02453, 3231.2016, 40.4115, 105.7817, planeDest, config.ticketPrice)
+ else
+ CreatePlane(1599.02453, 3231.2016, 40.4115, 105.7817, planeDest)
+ end
+ else
+ ShowNotification("No plane is ~y~scheduled~w~ to that location right now.")
+ end
+ end
+ end
+end
+
+AddAirPortMenu(mainMenu)
+_menuPool:RefreshIndex()
+
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(1)
+ _menuPool:ProcessMenus()
+ end
+end)
\ No newline at end of file
diff --git a/client/spawn.lua b/client/spawn.lua
new file mode 100644
index 0000000..491d84b
--- /dev/null
+++ b/client/spawn.lua
@@ -0,0 +1,81 @@
+--[[
+ FiveM Airports
+ Copyright (C) 2018 FiveM-Scripts
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this resource in the file "LICENSE". If not, see .
+]]
+
+function CreatePlane(x, y, z, heading, destination)
+ mainMenu:Visible(not mainMenu:Visible())
+
+ modelHash = GetHashKey(config.plane_model)
+ pilotModel = GetHashKey("s_m_m_pilot_01")
+
+ RequestModel(modelHash)
+ while not HasModelLoaded(modelHash) do
+ Citizen.Wait(0)
+ end
+
+ RequestModel(pilotModel)
+ while not HasModelLoaded(pilotModel) do
+ Citizen.Wait(0)
+ end
+
+ if HasModelLoaded(modelHash) and HasModelLoaded(pilotModel) then
+ ClearAreaOfEverything(x, y, z, 1500, false, false, false, false, false)
+
+ AirPlane = CreateVehicle(modelHash, x, y, z-1.0, heading, true, false)
+ SetVehicleOnGroundProperly(AirPlane)
+ SetVehicleEngineOn(AirPlane, true, true, true)
+ SetEntityProofs(PlayerPedId(), true, true, true, true, true, true, true, false)
+ SetVehicleHasBeenOwnedByPlayer(AirPlane, true)
+
+ pilot = CreatePedInsideVehicle(AirPlane, 6, pilotModel, -1, true, false)
+
+ SetBlockingOfNonTemporaryEvents(pilot, true)
+
+ local netVehid = NetworkGetNetworkIdFromEntity(AirPlane)
+ SetNetworkIdCanMigrate(netVehid, true)
+ NetworkRegisterEntityAsNetworked(VehToNet(AirPlane))
+
+ local netPedid = NetworkGetNetworkIdFromEntity(pilot)
+ SetNetworkIdCanMigrate(netPedid, true)
+ NetworkRegisterEntityAsNetworked(pilot)
+
+ totalSeats = GetVehicleModelNumberOfSeats(modelHash)
+ TaskWarpPedIntoVehicle(PlayerPedId(), AirPlane, 2)
+
+ SetModelAsNoLongerNeeded(modelHash)
+ SetModelAsNoLongerNeeded(pilotModel)
+ end
+
+ if destination == "DESRT" then
+ TaskPlaneMission(pilot, AirPlane, 0, 0, -107.2212, 2717.5534, 61.9673, 4, GetVehicleModelMaxSpeed(modelHash), 1.0, 0.0, 10.0, 40.0)
+ elseif destination == "AIRP" then
+ TaskVehicleDriveToCoordLongrange(pilot, AirPlane, 1403.0020751953, 2995.9179, 40.5507, GetVehicleModelMaxSpeed(modelHash), 16777216, 0.0)
+ Wait(5000)
+ TaskPlaneMission(pilot, AirPlane, 0, 0, -1571.5589, -556.7288, 114.4482, 4, GetVehicleModelMaxSpeed(modelHash), 1.0, 0.0, 5.0, 40.0)
+ end
+end
+
+RegisterNetEvent("airports:departure")
+AddEventHandler("airports:departure", function(x, y, z, heading, planeDest)
+ CreatePlane(x, y, z, heading, planeDest)
+end)
+
+RegisterNetEvent("airports:moneyInvalid")
+AddEventHandler("airports:moneyInvalid", function()
+ SetNotificationTextEntry("STRING")
+ AddTextComponentSubstringPlayerName("You don't have enough money to buy a ticket.\n")
+ SetNotificationMessage("CHAR_BLOCKED", "CHAR_BLOCKED", true, 4, "FiveM Airports", "", "You don't have enough money to buy a ticket.\n")
+ DrawNotification(false, true)
+end)
diff --git a/config.lua b/config.lua
new file mode 100644
index 0000000..a2d7e41
--- /dev/null
+++ b/config.lua
@@ -0,0 +1,6 @@
+config = {
+ plane_model = "nimbus",
+ use_essentialmode = true,
+ moneyCurrency = "$",
+ ticketPrice = 80,
+}
diff --git a/server/server.lua b/server/server.lua
new file mode 100644
index 0000000..fdc7cb4
--- /dev/null
+++ b/server/server.lua
@@ -0,0 +1,57 @@
+--[[
+ FiveM Airports
+ Copyright (C) 2018 FiveM-Scripts
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this resource in the file "LICENSE". If not, see .
+]]
+
+if GetCurrentResourceName() == 'airports' then
+ version = GetResourceMetadata(GetCurrentResourceName(), 'resource_version', 0)
+ PerformHttpRequest("https://updates.fivem-scripts.org/verify/airports", function(err, rData, headers)
+ if err == 404 then
+ RconPrint("\n----------------------------------------------------")
+ RconPrint("\nUPDATE ERROR: your version from FiveM Airports could not be verified.\n")
+ RconPrint("If you keep receiving this error then please contact FiveM-Scripts.")
+ RconPrint("\n----------------------------------------------------")
+ else
+ local vData = json.decode(rData)
+ if vData then
+ stableV = vData.version
+ if vData.version < version or vData.version > version then
+ RconPrint("\n----------------------------------------------------\n")
+ RconPrint("You are running a outdated version of FiveM Airports.\nPlease update to the most recent version: " .. vData.version)
+ RconPrint("\n----------------------------------------------------\n")
+ end
+ else
+ RconPrint("\n----------------------------------------------------------------------")
+ RconPrint("\nUPDATE ERROR: your version from FiveM Airports could not be verified.\n")
+ RconPrint("If you keep receiving this error then please contact FiveM-Scripts.")
+ RconPrint("\n----------------------------------------------------------------------\n")
+ end
+ end
+ end)
+end
+
+RegisterServerEvent("airports:payTicket")
+AddEventHandler("airports:payTicket", function(x, y, z, heading, destination, price)
+ TriggerEvent("es:getPlayerFromId", tonumber(source), function(user)
+ if user.getMoney() >= tonumber(price) then
+ user.removeMoney(tonumber(price))
+ TriggerClientEvent("airports:departure", tonumber(source), x, y, z, heading, destination)
+ elseif user.getBank() >= tonumber(price) then
+ user.removeBank(tonumber(price))
+ TriggerClientEvent("airports:departure", tonumber(source), x, y, z, heading, destination)
+ else
+ TriggerClientEvent("airports:moneyInvalid", tonumber(source))
+ end
+ end)
+end)
\ No newline at end of file