diff --git a/resources/[essential]/es_extended/__resource.lua b/resources/[essential]/es_extended/__resource.lua
new file mode 100644
index 00000000..4157559d
--- /dev/null
+++ b/resources/[essential]/es_extended/__resource.lua
@@ -0,0 +1,44 @@
+resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937'
+
+description 'ES Extended'
+
+server_scripts {
+ '@async/async.lua',
+ '@mysql-async/lib/MySQL.lua',
+ 'config.lua',
+ 'server/common.lua',
+ 'server/classes/player.lua',
+ 'server/functions.lua',
+ 'server/main.lua',
+ 'server/commands.lua'
+}
+
+client_scripts {
+ 'config.lua',
+ 'client/functions.lua',
+ 'client/main.lua'
+}
+
+ui_page {
+ 'html/ui.html'
+}
+
+files {
+
+ 'html/ui.html',
+
+ 'html/css/app.css',
+
+ 'html/js/mustache.min.js',
+ 'html/js/app.js',
+
+ 'html/fonts/pdown.ttf',
+ 'html/fonts/bankgothic.ttf',
+
+ 'html/img/cursor.png',
+ 'html/img/keys/enter.png',
+ 'html/img/keys/return.png',
+
+ 'html/img/accounts/bank.png',
+ 'html/img/accounts/black_money.png'
+}
\ No newline at end of file
diff --git a/resources/[essential]/es_extended/client/functions.lua b/resources/[essential]/es_extended/client/functions.lua
new file mode 100644
index 00000000..8e52a0b2
--- /dev/null
+++ b/resources/[essential]/es_extended/client/functions.lua
@@ -0,0 +1,781 @@
+local charset = {}
+
+for i = 48, 57 do table.insert(charset, string.char(i)) end
+for i = 65, 90 do table.insert(charset, string.char(i)) end
+for i = 97, 122 do table.insert(charset, string.char(i)) end
+
+ESX = {}
+ESX.CurrentRequestId = 0
+ESX.ServerCallbacks = {}
+ESX.TimeoutCallbacks = {}
+ESX.UI = {}
+ESX.UI.HUD = {}
+ESX.UI.HUD.RegisteredElements = {}
+ESX.UI.Menu = {}
+ESX.UI.Menu.RegisteredTypes = {}
+ESX.UI.Menu.Opened = {}
+ESX.Game = {}
+
+ESX.GetRandomString = function(length)
+
+ math.randomseed(GetGameTimer())
+
+ if length > 0 then
+ return ESX.GetRandomString(length - 1) .. charset[math.random(1, #charset)]
+ else
+ return ''
+ end
+
+end
+
+ESX.SetTimeout = function(msec, cb)
+ table.insert(ESX.TimeoutCallbacks, {
+ time = GetGameTimer() + msec,
+ cb = cb
+ })
+end
+
+ESX.ShowNotification = function(msg)
+ SetNotificationTextEntry('STRING')
+ AddTextComponentString(msg)
+ DrawNotification(0,1)
+end
+
+ESX.TriggerServerCallback = function(name, cb, a, b, c, d, e, f, g ,h ,i ,j ,k, l, m, n, o, p, q, r, s, t, u ,v ,w, x ,y ,z)
+
+ ESX.ServerCallbacks[ESX.CurrentRequestId] = cb
+
+ TriggerServerEvent('esx:triggerServerCallback', name, ESX.CurrentRequestId, a, b, c, d, e, f, g ,h ,i ,j ,k, l, m, n, o, p, q, r, s, t, u ,v ,w, x ,y ,z)
+
+ if ESX.CurrentRequestId < 65535 then
+ ESX.CurrentRequestId = ESX.CurrentRequestId + 1
+ else
+ ESX.CurrentRequestId = 0
+ end
+
+end
+
+ESX.UI.HUD.SetDisplay = function(opacity)
+
+ SendNUIMessage({
+ action = 'setHUDDisplay',
+ opacity = opacity
+ })
+
+end
+
+ESX.UI.HUD.RegisterElement = function(name, index, priority, html, data)
+
+ local found = false
+
+ for i=1, #ESX.UI.HUD.RegisteredElements, 1 do
+ if ESX.UI.HUD.RegisteredElements[i] == name then
+ found = true
+ break
+ end
+ end
+
+ if found then
+ return
+ end
+
+ table.insert(ESX.UI.HUD.RegisteredElements, name)
+
+ SendNUIMessage({
+ action = 'insertHUDElement',
+ name = name,
+ index = index,
+ priority = priority,
+ html = html,
+ data = data,
+ })
+
+ ESX.UI.HUD.UpdateElement(name, data)
+
+end
+
+ESX.UI.HUD.RemoveElement = function(name)
+
+ for i=1, #ESX.UI.HUD.RegisteredElements, 1 do
+ if ESX.UI.HUD.RegisteredElements[i] == name then
+ table.remove(ESX.UI.HUD.RegisteredElements, i)
+ break
+ end
+ end
+
+ SendNUIMessage({
+ action = 'deleteHUDElement',
+ name = name
+ })
+
+end
+
+ESX.UI.HUD.UpdateElement = function(name, data)
+
+ SendNUIMessage({
+ action = 'updateHUDElement',
+ name = name,
+ data = data,
+ })
+
+end
+
+ESX.UI.Menu.RegisterType = function(type, open, close)
+
+ ESX.UI.Menu.RegisteredTypes[type] = {
+ open = open,
+ close = close,
+ }
+
+end
+
+ESX.UI.Menu.Open = function(type, namespace, name, data, submit, cancel, change)
+
+ local menu = {}
+
+ menu.type = type
+ menu.namespace = namespace
+ menu.name = name
+ menu.data = data
+ menu.submit = submit
+ menu.cancel = cancel
+ menu.change = change
+
+ menu.close = function()
+
+ ESX.UI.Menu.RegisteredTypes[type].close(namespace, name)
+
+ for i=1, #ESX.UI.Menu.Opened, 1 do
+ if ESX.UI.Menu.Opened[i].type == type and ESX.UI.Menu.Opened[i].namespace == namespace and ESX.UI.Menu.Opened[i].name == name then
+ ESX.UI.Menu.Opened[i] = nil
+ end
+ end
+
+ end
+
+ menu.update = function(query, newData)
+
+ for i=1, #menu.data.elements, 1 do
+
+ local match = true
+
+ for k,v in pairs(query) do
+ if menu.data.elements[i][k] ~= v then
+ match = false
+ end
+ end
+
+ if match then
+ for k,v in pairs(newData) do
+ menu.data.elements[i][k] = v
+ end
+ end
+
+ ESX.UI.Menu.RegisteredTypes[type].open(namespace, name, menu.data)
+
+ end
+
+ end
+
+ menu.setElement = function(i, key, val)
+ menu.data.elements[i][key] = val
+ end
+
+ table.insert(ESX.UI.Menu.Opened, menu)
+
+ ESX.UI.Menu.RegisteredTypes[type].open(namespace, name, data)
+
+end
+
+ESX.UI.Menu.Close = function(type, namespace, name)
+
+ for i=1, #ESX.UI.Menu.Opened, 1 do
+ if ESX.UI.Menu.Opened[i].type == type and ESX.UI.Menu.Opened[i].namespace == namespace and ESX.UI.Menu.Opened[i].name == name then
+ ESX.UI.Menu.Opened[i].close()
+ ESX.UI.Menu.Opened[i] = nil
+ end
+ end
+
+end
+
+ESX.UI.Menu.CloseAll = function()
+
+ for i=1, #ESX.UI.Menu.Opened, 1 do
+ ESX.UI.Menu.Opened[i].close()
+ ESX.UI.Menu.Opened[i] = nil
+ end
+
+end
+
+ESX.UI.Menu.GetOpened = function(type, namespace, name)
+
+ for i=1, #ESX.UI.Menu.Opened, 1 do
+ if ESX.UI.Menu.Opened[i].type == type and ESX.UI.Menu.Opened[i].namespace == namespace and ESX.UI.Menu.Opened[i].name == name then
+ return ESX.UI.Menu.Opened[i]
+ end
+ end
+
+end
+
+ESX.UI.Menu.IsOpen = function(type, namespace, name)
+ return ESX.UI.Menu.GetOpened(type, namespace, name) ~= nil
+end
+
+ESX.UI.ShowInventoryItemNotification = function(add, item, count)
+ SendNUIMessage({
+ action = 'inventoryNotification',
+ add = add,
+ item = item,
+ count = count
+ })
+end
+
+ESX.GetWeaponList = function()
+ return Config.Weapons
+end
+
+ESX.GetWeaponLabel = function(name)
+
+ name = string.upper(name)
+ local weapons = ESX.GetWeaponList()
+
+ for i=1, #weapons, 1 do
+ if weapons[i].name == name then
+ return weapons[i].label
+ end
+ end
+
+end
+
+ESX.Game.GetPlayers = function()
+
+ local maxPlayers = Config.MaxPlayers
+ local players = {}
+
+ for i=0, maxPlayers, 1 do
+
+ local ped = GetPlayerPed(i)
+
+ if DoesEntityExist(ped) then
+ table.insert(players, i)
+ end
+ end
+
+ return players
+
+end
+
+ESX.Game.SpawnVehicle = function(modelName, coords, heading, cb)
+
+ local playerPed = GetPlayerPed(-1)
+ local model = (type(modelName) == 'number' and modelName or GetHashKey(modelName))
+
+ Citizen.CreateThread(function()
+
+ RequestModel(model)
+
+ while not HasModelLoaded(model) do
+ Citizen.Wait(0)
+ end
+
+ local vehicle = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false)
+
+ SetNetworkIdCanMigrate(id, true)
+ SetEntityAsMissionEntity(vehicle, true, true)
+ SetVehicleHasBeenOwnedByPlayer(vehicle, true)
+ SetModelAsNoLongerNeeded(model)
+
+ RequestCollisionAtCoord(coords.x, coords.y, coords.z)
+
+ while not HasCollisionLoadedAroundEntity(vehicle) do
+ RequestCollisionAtCoord(coords.x, coords.x, coords.x)
+ Citizen.Wait(0)
+ end
+
+ local id = NetworkGetNetworkIdFromEntity(vehicle)
+
+ if cb ~= nil then
+ cb(vehicle)
+ end
+
+ end)
+
+end
+
+ESX.Game.SpawnLocalVehicle = function(modelName, coords, heading, cb)
+
+ local playerPed = GetPlayerPed(-1)
+ local model = (type(modelName) == 'number' and modelName or GetHashKey(modelName))
+
+ Citizen.CreateThread(function()
+
+ RequestModel(model)
+
+ while not HasModelLoaded(model) do
+ Citizen.Wait(0)
+ end
+
+ local vehicle = CreateVehicle(model, coords.x, coords.y, coords.z, heading, false, false)
+ local id = NetworkGetNetworkIdFromEntity(vehicle)
+
+ SetNetworkIdCanMigrate(id, true)
+ SetEntityAsMissionEntity(vehicle, true, true)
+ SetVehicleHasBeenOwnedByPlayer(vehicle, true)
+ SetModelAsNoLongerNeeded(model)
+
+ RequestCollisionAtCoord(coords.x, coords.y, coords.z)
+
+ while not HasCollisionLoadedAroundEntity(vehicle) do
+ RequestCollisionAtCoord(coords.x, coords.x, coords.x)
+ Citizen.Wait(0)
+ end
+
+ if cb ~= nil then
+ cb(vehicle)
+ end
+
+ end)
+
+end
+
+ESX.Game.GetClosestPlayer = function()
+
+ local players = ESX.Game.GetPlayers()
+ local closestDistance = -1
+ local closestPlayer = -1
+ local playerPed = GetPlayerPed(-1)
+ local playerCoords = GetEntityCoords(playerPed)
+
+ for i=1, #players, 1 do
+
+ local target = GetPlayerPed(players[i])
+
+ -- if target ~= playerPed then
+
+ local targetCoords = GetEntityCoords(target)
+ local distance = GetDistanceBetweenCoords(targetCoords.x, targetCoords.y, targetCoords.z, playerCoords.x, playerCoords.y, playerCoords.z, true)
+
+ if closestDistance == -1 or closestDistance > distance then
+ closestPlayer = players[i]
+ closestDistance = distance
+ end
+
+ -- end
+
+ end
+
+ return closestPlayer, closestDistance
+end
+
+ESX.Game.GetPlayersInArea = function(coords, area)
+
+ local players = ESX.Game.GetPlayers()
+ local playersInArea = {}
+
+ for i=1, #players, 1 do
+
+ local target = GetPlayerPed(players[i])
+ local targetCoords = GetEntityCoords(target)
+ local distance = GetDistanceBetweenCoords(targetCoords.x, targetCoords.y, targetCoords.z, coords.x, coords.y, coords.z, true)
+
+ if distance <= area then
+ table.insert(playersInArea, players[i])
+ end
+
+ end
+
+ return playersInArea
+end
+
+ESX.Game.GetVehicleProperties = function(vehicle)
+
+ local colour1, colour2 = GetVehicleColours(vehicle)
+ local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle)
+
+ return {
+
+ model = GetEntityModel(vehicle),
+
+ plate = GetVehicleNumberPlateText(vehicle),
+ plateIndex = GetVehicleNumberPlateTextIndex(vehicle),
+
+ health = GetEntityHealth(vehicle),
+ dirtLevel = GetVehicleDirtLevel(vehicle),
+
+ color1 = colour1,
+ color2 = colour2,
+ pearlescentColor = pearlescentColor,
+ wheelColor = wheelColor,
+
+ wheels = GetVehicleWheelType(vehicle),
+ windowTint = GetVehicleWindowTint(vehicle),
+
+ neonColor = table.pack(GetVehicleNeonLightsColour(vehicle)),
+
+ modSpoilers = GetVehicleMod(vehicle, 0),
+ modFrontBumper = GetVehicleMod(vehicle, 1),
+ modRearBumper = GetVehicleMod(vehicle, 2),
+ modSideSkirt = GetVehicleMod(vehicle, 3),
+ modExhaust = GetVehicleMod(vehicle, 4),
+ modFrame = GetVehicleMod(vehicle, 5),
+ modGrille = GetVehicleMod(vehicle, 6),
+ modHood = GetVehicleMod(vehicle, 7),
+ modFender = GetVehicleMod(vehicle, 8),
+ modRightFender = GetVehicleMod(vehicle, 9),
+ modRoof = GetVehicleMod(vehicle, 10),
+
+ modEngine = GetVehicleMod(vehicle, 11),
+ modBrakes = GetVehicleMod(vehicle, 12),
+ modTransmission = GetVehicleMod(vehicle, 13),
+ modHorns = GetVehicleMod(vehicle, 14),
+ modSuspension = GetVehicleMod(vehicle, 15),
+ modArmor = GetVehicleMod(vehicle, 16),
+
+ modTurbo = IsToggleModOn(vehicle, 18),
+ modXenon = IsToggleModOn(vehicle, 22),
+
+ modFrontWheels = GetVehicleMod(vehicle, 23),
+ modBackWheels = GetVehicleMod(vehicle, 24)
+ }
+
+end
+
+ESX.Game.SetVehicleProperties = function(vehicle, props)
+
+ SetVehicleModKit(vehicle, 0)
+
+ if props.plate ~= nil then
+ SetVehicleNumberPlateText(vehicle, props.plate)
+ end
+
+ if props.plateIndex ~= nil then
+ SetVehicleNumberPlateTextIndex(vehicle, props.plateIndex)
+ end
+
+ if props.health ~= nil then
+ SetEntityHealth(vehicle, props.health)
+ end
+
+ if props.dirtLevel ~= nil then
+ SetVehicleDirtLevel(vehicle, props.dirtLevel)
+ end
+
+ if props.color1 ~= nil and props.color2 ~= nil then
+ SetVehicleColours(vehicle, props.color1, props.color2)
+ end
+
+ if props.pearlescentColor ~= nil and props.wheelColor ~= nil then
+ SetVehicleExtraColours(vehicle, props.pearlescentColor, props.wheelColor)
+ end
+
+ if props.wheels ~= nil then
+ SetVehicleWheelType(vehicle, props.wheels)
+ end
+
+ if props.windowTint ~= nil then
+ SetVehicleWindowTint(vehicle, props.windowTint)
+ end
+
+ if props.neonColor ~= nil then
+ SetVehicleNeonLightsColour(vehicle, props.neonColor[1], props.neonColor[2], props.neonColor[3])
+ end
+
+ if props.modSpoilers ~= nil then
+ SetVehicleMod(vehicle, 0, props.modSpoilers, false)
+ end
+
+ if props.modFrontBumper ~= nil then
+ SetVehicleMod(vehicle, 1, props.modFrontBumper, false)
+ end
+
+ if props.modRearBumper ~= nil then
+ SetVehicleMod(vehicle, 2, props.modRearBumper, false)
+ end
+
+ if props.modSideSkirt ~= nil then
+ SetVehicleMod(vehicle, 3, props.modSideSkirt, false)
+ end
+
+ if props.modExhaust ~= nil then
+ SetVehicleMod(vehicle, 4, props.modExhaust, false)
+ end
+
+ if props.modFrame ~= nil then
+ SetVehicleMod(vehicle, 5, props.modFrame, false)
+ end
+
+ if props.modGrille ~= nil then
+ SetVehicleMod(vehicle, 6, props.modGrille, false)
+ end
+
+ if props.modHood ~= nil then
+ SetVehicleMod(vehicle, 7, props.modHood, false)
+ end
+
+ if props.modFender ~= nil then
+ SetVehicleMod(vehicle, 8, props.modFender, false)
+ end
+
+ if props.modRightFender ~= nil then
+ SetVehicleMod(vehicle, 9, props.modRightFender, false)
+ end
+
+ if props.modRoof ~= nil then
+ SetVehicleMod(vehicle, 10, props.modRoof, false)
+ end
+
+ if props.modEngine ~= nil then
+ SetVehicleMod(vehicle, 11, props.modEngine, false)
+ end
+
+ if props.modBrakes ~= nil then
+ SetVehicleMod(vehicle, 12, props.modBrakes, false)
+ end
+
+ if props.modTransmission ~= nil then
+ SetVehicleMod(vehicle, 13, props.modTransmission, false)
+ end
+
+ if props.modHorns ~= nil then
+ SetVehicleMod(vehicle, 14, props.modHorns, false)
+ end
+
+ if props.modSuspension ~= nil then
+ SetVehicleMod(vehicle, 15, props.modSuspension, false)
+ end
+
+ if props.modArmor ~= nil then
+ SetVehicleMod(vehicle, 16, props.modArmor, false)
+ end
+
+ if props.modTurbo ~= nil then
+ ToggleVehicleMod(vehicle, 18, props.modTurbo)
+ end
+
+ if props.modXenon ~= nil then
+ ToggleVehicleMod(vehicle, 22, props.modXenon)
+ end
+
+ if props.modFrontWheels ~= nil then
+ SetVehicleMod(vehicle, 23, props.modFrontWheels, false)
+ end
+
+ if props.modBackWheels ~= nil then
+ SetVehicleMod(vehicle, 24, props.modBackWheels, false)
+ end
+
+end
+
+ESX.ShowInventory = function()
+
+ ESX.TriggerServerCallback('esx:getPlayerData', function(data)
+
+ local playerPed = GetPlayerPed(-1)
+ local elements = {}
+
+ table.insert(elements, {
+ label = '[Cash] $' .. data.money,
+ count = data.money,
+ type = 'item_money',
+ value = 'money',
+ usable = false
+ })
+
+ for i=1, #data.accounts, 1 do
+ table.insert(elements, {
+ label = '[' .. data.accounts[i].label .. '] $' .. data.accounts[i].money,
+ count = data.accounts[i].money,
+ type = 'item_account',
+ value = data.accounts[i].name,
+ usable = false
+ })
+ end
+
+ for i=1, #data.inventory, 1 do
+
+ if data.inventory[i].count > 0 then
+ table.insert(elements, {
+ label = data.inventory[i].label .. ' x' .. data.inventory[i].count,
+ count = data.inventory[i].count,
+ type = 'item_standard',
+ value = data.inventory[i].name,
+ usable = data.inventory[i].usable
+ })
+ end
+
+ end
+
+ for i=1, #Config.Weapons, 1 do
+
+ local weaponHash = GetHashKey(Config.Weapons[i].name)
+
+ if HasPedGotWeapon(playerPed, weaponHash, false) and Config.Weapons[i].name ~= 'WEAPON_UNARMED' then
+
+ local ammo = GetAmmoInPedWeapon(playerPed, weaponHash)
+
+ table.insert(elements, {
+ label = Config.Weapons[i].label .. ' x1 [' .. ammo .. ']',
+ count = 1,
+ type = 'item_weapon',
+ value = Config.Weapons[i].name,
+ usable = false
+ })
+
+ end
+ end
+
+ ESX.UI.Menu.CloseAll()
+
+ ESX.UI.Menu.Open(
+ 'default', GetCurrentResourceName(), 'inventory',
+ {
+ title = 'Inventaire',
+ align = 'bottom-right',
+ elements = elements,
+ },
+ function(data, menu)
+
+ menu.close()
+
+ local elements = {}
+
+ if data.current.usable then
+ table.insert(elements, {label = 'Utiliser', action = 'use', type = data.current.type, value = data.current.value})
+ end
+
+ table.insert(elements, {label = 'Donner', action = 'give', type = data.current.type, value = data.current.value})
+ table.insert(elements, {label = 'Jeter', action = 'remove', type = data.current.type, value = data.current.value})
+ table.insert(elements, {label = 'Retour', action = 'return'})
+
+ ESX.UI.Menu.Open(
+ 'default', GetCurrentResourceName(), 'inventory_item',
+ {
+ title = 'Inventaire',
+ align = 'bottom-right',
+ elements = elements,
+ },
+ function(data, menu)
+
+ local item = data.current.value
+ local type = data.current.type
+
+ if data.current.action == 'give' then
+
+ if type == 'item_weapon' then
+ TriggerServerEvent('esx:giveInventoryItem', GetPlayerServerId(closestPlayer), type, item, 1)
+ else
+
+ ESX.UI.Menu.Open(
+ 'dialog', GetCurrentResourceName(), 'inventory_item_count_give',
+ {
+ title = 'Quantité'
+ },
+ function(data2, menu)
+
+ local quantity = tonumber(data2.value)
+ local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer()
+
+ if closestPlayer == -1 or closestDistance > 3.0 then
+ ESX.ShowNotification('Aucun joueur à proximité')
+ else
+ TriggerServerEvent('esx:giveInventoryItem', GetPlayerServerId(closestPlayer), type, item, quantity)
+ end
+
+ menu.close()
+ end,
+ function(data2, menu)
+ menu.close()
+ end
+ )
+
+ end
+
+ elseif data.current.action == 'remove' then
+
+ if type == 'item_weapon' then
+ TriggerServerEvent('esx:removeInventoryItem', type, item, 1)
+ else
+
+ ESX.UI.Menu.Open(
+ 'dialog', GetCurrentResourceName(), 'inventory_item_count_remove',
+ {
+ title = 'Quantité'
+ },
+ function(data2, menu)
+
+ local quantity = tonumber(data2.value)
+
+ if quantity == nil then
+ ESX.ShowNotification('Montant invalide')
+ else
+
+ menu.close()
+
+ TriggerServerEvent('esx:removeInventoryItem', type, item, quantity)
+
+ end
+
+ end,
+ function(data2, menu)
+ menu.close()
+ end
+ )
+
+ end
+
+ elseif data.current.action == 'return' then
+ ESX.UI.Menu.CloseAll()
+ ESX.ShowInventory()
+ end
+
+ end,
+ function(data, menu)
+ ESX.UI.Menu.CloseAll()
+ ESX.ShowInventory()
+ end
+ )
+
+ end,
+ function(data, menu)
+ menu.close()
+ end
+ )
+
+ end)
+
+end
+
+AddEventHandler('esx:getSharedObject', function(cb)
+ cb(ESX)
+end)
+
+RegisterNetEvent('esx:serverCallback')
+AddEventHandler('esx:serverCallback', function(requestId, a, b, c, d, e, f, g ,h ,i ,j ,k, l, m, n, o, p, q, r, s, t, u ,v ,w, x ,y ,z)
+ ESX.ServerCallbacks[requestId](a, b, c, d, e, f, g ,h ,i ,j ,k, l, m, n, o, p, q, r, s, t, u ,v ,w, x ,y ,z)
+ ESX.ServerCallbacks[requestId] = nil
+end)
+
+RegisterNetEvent('esx:showNotification')
+AddEventHandler('esx:showNotification', function(msg)
+ ESX.ShowNotification(msg)
+end)
+
+-- SetTimeout
+Citizen.CreateThread(function()
+ while true do
+
+ Citizen.Wait(0)
+
+ local currTime = GetGameTimer()
+
+ for i=1, #ESX.TimeoutCallbacks, 1 do
+
+ if currTime >= ESX.TimeoutCallbacks[i].time then
+ ESX.TimeoutCallbacks[i].cb()
+ ESX.TimeoutCallbacks[i] = nil
+ end
+
+ end
+
+ end
+end)
\ No newline at end of file
diff --git a/resources/[essential]/es_extended/client/main.lua b/resources/[essential]/es_extended/client/main.lua
new file mode 100644
index 00000000..a2e1adb1
--- /dev/null
+++ b/resources/[essential]/es_extended/client/main.lua
@@ -0,0 +1,372 @@
+local Keys = {
+ ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57,
+ ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177,
+ ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18,
+ ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182,
+ ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81,
+ ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70,
+ ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178,
+ ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173,
+ ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118
+}
+
+local GUI = {}
+GUI.Time = 0
+local PlayerLoaded = false
+local LoadoutLoaded = false
+local IsPaused = false
+local LastLoadout = {}
+
+RegisterNetEvent('esx:playerLoaded')
+AddEventHandler('esx:playerLoaded', function(xPlayer)
+
+ PlayerLoaded = true
+
+ for i=1, #xPlayer.accounts, 1 do
+
+ local accountTpl = '

{{money}}
'
+
+ ESX.UI.HUD.RegisterElement('account_' .. xPlayer.accounts[i].name, i-1, 0, accountTpl, {
+ money = 0
+ })
+
+ ESX.UI.HUD.UpdateElement('account_' .. xPlayer.accounts[i].name, {
+ money = xPlayer.accounts[i].money
+ })
+
+ end
+
+ local jobTpl = '{{job_label}} - {{grade_label}}
'
+
+ if xPlayer.job.grade_label == '' then
+ jobTpl = '{{job_label}}
'
+ end
+
+ ESX.UI.HUD.RegisterElement('job', #xPlayer.accounts, 0, jobTpl, {
+ job_label = '',
+ grade_label = ''
+ })
+
+ ESX.UI.HUD.UpdateElement('job', {
+ job_label = xPlayer.job.label,
+ grade_label = xPlayer.job.grade_label
+ })
+
+end)
+
+AddEventHandler('playerSpawned', function()
+
+ Citizen.CreateThread(function()
+
+ while not PlayerLoaded do
+ Citizen.Wait(0)
+ end
+
+ ESX.TriggerServerCallback('esx:getPlayerData', function(data)
+
+ local playerPed = GetPlayerPed(-1)
+
+ -- Restore position
+ if data.lastPosition ~= nil then
+ SetEntityCoords(playerPed, data.lastPosition.x, data.lastPosition.y, data.lastPosition.z)
+ end
+
+ -- Restore loadout
+ for i=1, #data.loadout, 1 do
+ local weaponHash = GetHashKey(data.loadout[i].name)
+ GiveWeaponToPed(playerPed, weaponHash, data.loadout[i].ammo, false, false)
+ end
+
+ LoadoutLoaded = true
+
+ end)
+
+ end)
+
+end)
+
+AddEventHandler('skinchanger:LoadDefaultModel', function()
+ LoadoutLoaded = false
+end)
+
+AddEventHandler('skinchanger:modelLoaded', function()
+
+ -- Restore loadout
+ ESX.TriggerServerCallback('esx:getPlayerData', function(data)
+
+ local playerPed = GetPlayerPed(-1)
+
+ for i=1, #data.loadout, 1 do
+ local weaponHash = GetHashKey(data.loadout[i].name)
+ GiveWeaponToPed(playerPed, weaponHash, data.loadout[i].ammo, false, false)
+ end
+
+ LoadoutLoaded = true
+
+ end)
+
+end)
+
+RegisterNetEvent('esx:setAccountMoney')
+AddEventHandler('esx:setAccountMoney', function(account)
+ ESX.UI.HUD.UpdateElement('account_' .. account.name, {
+ money = account.money
+ })
+end)
+
+RegisterNetEvent('esx:addInventoryItem')
+AddEventHandler('esx:addInventoryItem', function(item, count)
+
+ ESX.UI.ShowInventoryItemNotification(true, item, count)
+
+ if ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') then
+ ESX.ShowInventory()
+ end
+
+end)
+
+RegisterNetEvent('esx:removeInventoryItem')
+AddEventHandler('esx:removeInventoryItem', function(item, count)
+
+ ESX.UI.ShowInventoryItemNotification(false, item, count)
+
+ if ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') then
+ ESX.ShowInventory()
+ end
+
+end)
+
+RegisterNetEvent('esx:addWeapon')
+AddEventHandler('esx:addWeapon', function(weaponName, ammo)
+ local playerPed = GetPlayerPed(-1)
+ local weaponHash = GetHashKey(weaponName)
+
+ GiveWeaponToPed(playerPed, weaponHash, ammo, false, false)
+end)
+
+RegisterNetEvent('esx:removeWeapon')
+AddEventHandler('esx:removeWeapon', function(weaponName)
+ local playerPed = GetPlayerPed(-1)
+ local weaponHash = GetHashKey(weaponName)
+
+ RemoveWeaponFromPed(playerPed, weaponHash)
+end)
+
+-- Commands
+RegisterNetEvent('esx:teleport')
+AddEventHandler('esx:teleport', function(pos)
+
+ pos.x = pos.x + 0.0
+ pos.y = pos.y + 0.0
+ pos.z = pos.z + 0.0
+
+ RequestCollisionAtCoord(pos.x, pos.y, pos.z)
+
+ while not HasCollisionLoadedAroundEntity(GetPlayerPed(-1)) do
+ RequestCollisionAtCoord(pos.x, pos.y, pos.z)
+ Citizen.Wait(0)
+ end
+
+ SetEntityCoords(GetPlayerPed(-1), pos.x, pos.y, pos.z)
+
+end)
+
+RegisterNetEvent('esx:setJob')
+AddEventHandler('esx:setJob', function(job)
+ ESX.UI.HUD.UpdateElement('job', {
+ job_label = job.label,
+ grade_label = job.grade_label
+ })
+end)
+
+RegisterNetEvent('esx:loadIPL')
+AddEventHandler('esx:loadIPL', function(name)
+
+ Citizen.CreateThread(function()
+ LoadMpDlcMaps()
+ EnableMpDlcMaps(true)
+ RequestIpl(name)
+ end)
+
+end)
+
+RegisterNetEvent('esx:unloadIPL')
+AddEventHandler('esx:unloadIPL', function(name)
+
+ Citizen.CreateThread(function()
+ RemoveIpl(name)
+ end)
+
+end)
+
+RegisterNetEvent('esx:playAnim')
+AddEventHandler('esx:playAnim', function(dict, anim)
+
+ Citizen.CreateThread(function()
+
+ local pid = PlayerPedId()
+
+ RequestAnimDict(dict)
+
+ while not HasAnimDictLoaded(dict) do
+ Wait(0)
+ end
+
+ TaskPlayAnim(pid, dict, anim, 1.0, -1.0, 20000, 0, 1, true, true, true)
+
+ end)
+
+end)
+
+RegisterNetEvent('esx:playEmote')
+AddEventHandler('esx:playEmote', function(emote)
+
+ Citizen.CreateThread(function()
+
+ local playerPed = GetPlayerPed(-1)
+
+ TaskStartScenarioInPlace(playerPed, emote, 0, false);
+ Wait(20000)
+ ClearPedTasks(playerPed)
+
+ end)
+
+end)
+
+RegisterNetEvent('esx:spawnVehicle')
+AddEventHandler('esx:spawnVehicle', function(model)
+
+ local playerPed = GetPlayerPed(-1)
+ local coords = GetEntityCoords(playerPed)
+
+ ESX.Game.SpawnVehicle(model, coords, 90.0, function(vehicle)
+ TaskWarpPedIntoVehicle(playerPed, vehicle, -1)
+ end)
+
+end)
+
+-- Pause menu disable HUD display
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(1)
+ if IsPauseMenuActive() and not IsPaused then
+ IsPaused = true
+ TriggerEvent('es:setMoneyDisplay', 0.0)
+ ESX.UI.HUD.SetDisplay(0.0)
+ elseif not IsPauseMenuActive() and IsPaused then
+ IsPaused = false
+ TriggerEvent('es:setMoneyDisplay', 1.0)
+ ESX.UI.HUD.SetDisplay(1.0)
+ end
+ end
+end)
+
+-- Save loadout
+Citizen.CreateThread(function()
+
+ while true do
+
+ Wait(0)
+
+ local playerPed = GetPlayerPed(-1)
+ local loadout = {}
+ local loadoutChanged = false
+
+ if IsPedDeadOrDying(playerPed) then
+ LoadoutLoaded = false
+ end
+
+ for i=1, #Config.Weapons, 1 do
+
+ local weaponHash = GetHashKey(Config.Weapons[i].name)
+
+ if HasPedGotWeapon(playerPed, weaponHash, false) and Config.Weapons[i].name ~= 'WEAPON_UNARMED' then
+
+ local ammo = GetAmmoInPedWeapon(playerPed, weaponHash)
+
+ if LastLoadout[Config.Weapons[i].name] == nil or LastLoadout[Config.Weapons[i].name] ~= ammo then
+ loadoutChanged = true
+ end
+
+ LastLoadout[Config.Weapons[i].name] = ammo
+
+ table.insert(loadout, {
+ name = Config.Weapons[i].name,
+ ammo = ammo,
+ label = Config.Weapons[i].label
+ })
+
+ else
+
+ if LastLoadout[Config.Weapons[i].name] ~= nil then
+ loadoutChanged = true
+ end
+
+ LastLoadout[Config.Weapons[i].name] = nil
+
+ end
+
+ end
+
+ if loadoutChanged and LoadoutLoaded then
+ TriggerServerEvent('esx:updateLoadout', loadout)
+ end
+
+ end
+end)
+
+-- Menu interactions
+Citizen.CreateThread(function()
+ while true do
+
+ Wait(0)
+
+ if IsControlPressed(0, Keys["F2"]) and not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') and (GetGameTimer() - GUI.Time) > 150 then
+ ESX.ShowInventory()
+ GUI.Time = GetGameTimer()
+ end
+
+ end
+end)
+
+-- Dot above head
+if Config.ShowDotAbovePlayer then
+
+ Citizen.CreateThread(function()
+ while true do
+
+ Citizen.Wait(0)
+
+ local players = ESX.Game.GetPlayers()
+
+ for i = 1, #players, 1 do
+ if players[i] ~= PlayerId() then
+ local ped = GetPlayerPed(players[i])
+ local headId = Citizen.InvokeNative(0xBFEFE3321A3F5015, ped, ('·'), false, false, '', false)
+ end
+ end
+
+ end
+ end)
+
+end
+
+-- Disable wanted level
+if Config.DisableWantedLevel then
+
+ Citizen.CreateThread(function()
+ while true do
+
+ Citizen.Wait(0)
+
+ local playerId = PlayerId()
+
+ if GetPlayerWantedLevel(playerId) ~= 0 then
+ SetPlayerWantedLevel(playerId, 0, false)
+ SetPlayerWantedLevelNow(playerId, false)
+ end
+
+ end
+ end)
+
+end
\ No newline at end of file
diff --git a/resources/[essential]/es_extended/config.lua b/resources/[essential]/es_extended/config.lua
new file mode 100644
index 00000000..22f1c7c9
--- /dev/null
+++ b/resources/[essential]/es_extended/config.lua
@@ -0,0 +1,89 @@
+Config = {}
+Config.MaxPlayers = 32
+Config.Accounts = {'bank', 'black_money'}
+Config.AccountLabels = {bank = 'Banque', black_money = 'Argent Sale'}
+Config.PaycheckInterval = 7 * 60000
+Config.ShowDotAbovePlayer = false
+Config.DisableWantedLevel = true
+Config.RemoveInventoryItemDelay = 5 * 60000
+
+Config.Weapons = {
+
+ {name = 'WEAPON_KNIFE', label = 'Couteau'},
+ {name = 'WEAPON_NIGHTSTICK', label = 'Matraque'},
+ {name = 'WEAPON_HAMMER', label = 'Marteau'},
+ {name = 'WEAPON_BAT', label = 'Bat'},
+ {name = 'WEAPON_GOLFCLUB', label = 'Club de golfe'},
+ {name = 'WEAPON_CROWBAR', label = 'Pied de biche'},
+ {name = 'WEAPON_PISTOL', label = 'Pistolet'},
+ {name = 'WEAPON_COMBATPISTOL', label = 'Pistolet de combat'},
+ {name = 'WEAPON_APPISTOL', label = 'Pistolet automatique'},
+ {name = 'WEAPON_PISTOL50', label = 'Pistolet calibre 50'},
+ {name = 'WEAPON_MICROSMG', label = 'Micro SMG'},
+ {name = 'WEAPON_SMG', label = 'SMG'},
+ {name = 'WEAPON_ASSAULTSMG', label = 'SMG d\'assaut'},
+ {name = 'WEAPON_ASSAULTRIFLE', label = 'Fusil d\'assaut'},
+ {name = 'WEAPON_CARBINERIFLE', label = 'Carabine d\'assaut'},
+ {name = 'WEAPON_ADVANCEDRIFLE', label = 'Fusil avancé'},
+ {name = 'WEAPON_MG', label = 'Mitrailleuse'},
+ {name = 'WEAPON_COMBATMG', label = 'Mitrailleuse de combat'},
+ {name = 'WEAPON_PUMPSHOTGUN', label = 'Fusil à pompe'},
+ {name = 'WEAPON_SAWNOFFSHOTGUN', label = 'Carabine à canon scié'},
+ {name = 'WEAPON_ASSAULTSHOTGUN', label = 'Carabine d\'assaut'},
+ {name = 'WEAPON_BULLPUPSHOTGUN', label = 'Carabine Bullpup'},
+ {name = 'WEAPON_STUNGUN', label = 'Tazer'},
+ {name = 'WEAPON_SNIPERRIFLE', label = 'Fusil de sniper'},
+ {name = 'WEAPON_HEAVYSNIPER', label = 'Fusil de sniper lourd'},
+ {name = 'WEAPON_REMOTESNIPER', label = 'Fusil de sniper à distance'},
+ {name = 'WEAPON_GRENADELAUNCHER', label = 'Lance-grenade'},
+ {name = 'WEAPON_RPG', label = 'Lance-rocket'},
+ {name = 'WEAPON_STINGER', label = 'Lance-Missile Stinger'},
+ {name = 'WEAPON_MINIGUN', label = 'Minigun'},
+ {name = 'WEAPON_GRENADE', label = 'Grenade'},
+ {name = 'WEAPON_STICKYBOMB', label = 'Bombe collante'},
+ {name = 'WEAPON_SMOKEGRENADE', label = 'Grenade fumigène'},
+ {name = 'WEAPON_BZGAS', 'Grenade à gaz BZ'},
+ {name = 'WEAPON_MOLOTOV', 'Cocktail molotov'},
+ {name = 'WEAPON_FIREEXTINGUISHER', 'Extincteur'},
+ {name = 'WEAPON_PETROLCAN', 'Jerrican d\'essence'},
+ {name = 'WEAPON_DIGISCANNER', 'Digiscanner'},
+ {name = 'WEAPON_BALL', 'Balle'},
+ {name = 'WEAPON_SNSPISTOL', label = 'Pistolet SNS'},
+ {name = 'WEAPON_BOTTLE', label = 'Bouteille'},
+ {name = 'WEAPON_GUSENBERG', label = 'Balayeuse Gusenberg'},
+ {name = 'WEAPON_SPECIALCARBINE', label = 'Carabine spéciale'},
+ {name = 'WEAPON_HEAVYPISTOL', label = 'Pistolet lourd'},
+ {name = 'WEAPON_BULLPUPRIFLE', label = 'Fusil Bullpup'},
+ {name = 'WEAPON_DAGGER', label = 'Poignard'},
+ {name = 'WEAPON_VINTAGEPISTOL', label = 'Pistolet vintage'},
+ {name = 'WEAPON_FIREWORK', label = 'Feu d\'artifice'},
+ {name = 'WEAPON_MUSKET', label = 'Mousquet'},
+ {name = 'WEAPON_HEAVYSHOTGUN', label = 'Fusil à pompe lourd'},
+ {name = 'WEAPON_MARKSMANRIFLE', label = 'Fusil Marksman'},
+ {name = 'WEAPON_HOMINGLAUNCHER', label = 'Lance tête-chercheuse'},
+ {name = 'WEAPON_PROXMINE', label = 'Mine de proximité'},
+ {name = 'WEAPON_SNOWBALL', label = 'Boule de neige'},
+ {name = 'WEAPON_FLAREGUN', label = 'Lance fusée de détresse'},
+ {name = 'WEAPON_GARBAGEBAG', label = 'Sac poubelle'},
+ {name = 'WEAPON_HANDCUFFS', label = 'Menottes'},
+ {name = 'WEAPON_COMBATPDW', label = 'Arme de défense personnelle'},
+ {name = 'WEAPON_MARKSMANPISTOL', label = 'Pistolet Marksman'},
+ {name = 'WEAPON_KNUCKLE', label = 'Poing américain'},
+ {name = 'WEAPON_HATCHET', label = 'Hachette'},
+ {name = 'WEAPON_RAILGUN', label = 'Canon éléctrique'},
+ {name = 'WEAPON_MACHETE', label = 'Machetta'},
+ {name = 'WEAPON_MACHINEPISTOL', label = 'Pistolet mitrailleur'},
+ {name = 'WEAPON_SWITCHBLADE', label = 'Couteau à cran d\'arrêt'},
+ {name = 'WEAPON_REVOLVER', label = 'Revolver'},
+ {name = 'WEAPON_DBSHOTGUN', label = 'Fusil à pompe double canon'},
+ {name = 'WEAPON_COMPACTRIFLE', label = 'Fusil compact'},
+ {name = 'WEAPON_AUTOSHOTGUN', label = 'Fusil à pompe automatique'},
+ {name = 'WEAPON_BATTLEAXE', label = 'Hache de combat'},
+ {name = 'WEAPON_COMPACTLAUNCHER', label = 'Lanceur compact'},
+ {name = 'WEAPON_MINISMG', label = 'Mini SMG'},
+ {name = 'WEAPON_PIPEBOMB', label = 'Bombe tuyau'},
+ {name = 'WEAPON_POOLCUE', label = 'Queue de billard'},
+ {name = 'WEAPON_WRENCH', label = 'Clé'},
+ {name = 'GADGET_NIGHTVISION', label = 'Vision nocturne'},
+ {name = 'GADGET_PARACHUTE', label = 'Parachute'},
+}
\ No newline at end of file
diff --git a/resources/[essential]/es_extended/html/css/app.css b/resources/[essential]/es_extended/html/css/app.css
new file mode 100644
index 00000000..2f72814b
--- /dev/null
+++ b/resources/[essential]/es_extended/html/css/app.css
@@ -0,0 +1,101 @@
+@font-face {
+ font-family: 'Pricedown';
+ src: url('../fonts/pdown.ttf') /* TTF file for CSS3 browsers */
+}
+
+@font-face {
+ font-family: 'bankgothic';
+ src: url('../fonts/bankgothic.ttf') /* TTF file for CSS3 browsers */
+}
+
+html {
+ overflow: hidden;
+}
+
+#cursor {
+ position: absolute;
+ z-index: 999999;
+ display: none;
+}
+
+#hud {
+ position : absolute;
+ font-family: 'Pricedown';
+ font-size : 35px;
+ color : white;
+ padding : 4px;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000;
+
+ text-align: right;
+ top : 80;
+ right : 40;
+}
+
+#inventory_notifications {
+ font-family: bankgothic;
+ position : absolute;
+ right : 40;
+ bottom : 40;
+ font-size : 2em;
+ font-weight: bold;
+ color : #FFF;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000;
+}
+
+.menu {
+ font-family: 'Open Sans', sans-serif;
+ min-width : 400px;
+ min-height: : 250px;
+ color : #fff;
+ position : absolute;
+ left : 40;
+ top : 0;
+}
+
+.menu .head {
+ font-family: 'Open Sans', sans-serif;
+ font-size: 28px;
+ padding: 10px;
+ background: #1A1A1A;
+ border-bottom: 3px solid #BC1635;
+ border-radius: 10px 10px 0 0;
+ -webkit-border-radius: 10px 10px 0 0;
+ -moz-border-radius: 10px 10px 0 0;
+ -o-border-radius: 10px 10px 0 0;
+ box-shadow: inset 0px 1px 0 rgba(255, 255, 255, 0.28);
+ -webkit-box-shadow: inset 0px 1px 0 rgba(255, 255, 255, 0.28);
+ -moz-box-shadow: inset 0px 1px 0 rgba(255, 255, 255, 0.28);
+ -o-box-shadow: inset 0px 1px 0 rgba(255, 255, 255, 0.28);
+ box-shadow: 1px 1px 10px 4px rgba(0, 0, 0, 0.4);
+}
+.menu .head span {
+ font-family: 'Pricedown';
+ font-size: 28px;
+ padding-left: 15px;
+ padding-top: 6px;
+}
+
+.menu .menu-items .menu-item {
+ font-family : 'Open Sans', sans-serif;
+ font-size : 14px;
+ height : 40px;
+ display : block;
+ background-color: #f1f1f1;
+ box-shadow : inset 1px 0px 0px 1px #b8b8b8;
+ height : 32px;
+ line-height : 32px;
+ color : #3A3A3A;
+ text-align : center;
+}
+
+.menu .menu-items .menu-item.selected {
+ background-color: #ccc;
+}
diff --git a/resources/[essential]/es_extended/html/fonts/bankgothic.ttf b/resources/[essential]/es_extended/html/fonts/bankgothic.ttf
new file mode 100644
index 00000000..f3d8049b
Binary files /dev/null and b/resources/[essential]/es_extended/html/fonts/bankgothic.ttf differ
diff --git a/resources/[essential]/es_extended/html/fonts/pdown.ttf b/resources/[essential]/es_extended/html/fonts/pdown.ttf
new file mode 100644
index 00000000..69c76cf3
Binary files /dev/null and b/resources/[essential]/es_extended/html/fonts/pdown.ttf differ
diff --git a/resources/[essential]/es_extended/html/img/accounts/bank.png b/resources/[essential]/es_extended/html/img/accounts/bank.png
new file mode 100644
index 00000000..eb36dc8e
Binary files /dev/null and b/resources/[essential]/es_extended/html/img/accounts/bank.png differ
diff --git a/resources/[essential]/es_extended/html/img/accounts/black_money.png b/resources/[essential]/es_extended/html/img/accounts/black_money.png
new file mode 100644
index 00000000..a42f6aa5
Binary files /dev/null and b/resources/[essential]/es_extended/html/img/accounts/black_money.png differ
diff --git a/resources/[essential]/es_extended/html/img/cursor.png b/resources/[essential]/es_extended/html/img/cursor.png
new file mode 100644
index 00000000..39536f95
Binary files /dev/null and b/resources/[essential]/es_extended/html/img/cursor.png differ
diff --git a/resources/[essential]/es_extended/html/img/keys/enter.png b/resources/[essential]/es_extended/html/img/keys/enter.png
new file mode 100644
index 00000000..3eb9a1f6
Binary files /dev/null and b/resources/[essential]/es_extended/html/img/keys/enter.png differ
diff --git a/resources/[essential]/es_extended/html/img/keys/return.png b/resources/[essential]/es_extended/html/img/keys/return.png
new file mode 100644
index 00000000..a3ca125a
Binary files /dev/null and b/resources/[essential]/es_extended/html/img/keys/return.png differ
diff --git a/resources/[essential]/es_extended/html/js/app.js b/resources/[essential]/es_extended/html/js/app.js
new file mode 100644
index 00000000..6da6d52b
--- /dev/null
+++ b/resources/[essential]/es_extended/html/js/app.js
@@ -0,0 +1,114 @@
+(() => {
+
+ ESX = {};
+ ESX.HUDElements = [];
+
+ ESX.setHUDDisplay = function(opacity){
+ $('#hud').css('opacity', opacity);
+ }
+
+ ESX.insertHUDElement = function(name, index, priority, html, data){
+
+ ESX.HUDElements.push({
+ name : name,
+ index : index,
+ priority : priority,
+ html : html,
+ data : data
+ })
+
+ ESX.HUDElements.sort((a,b) => {
+ return a.index - b.index || b.priority - a.priority;
+ })
+
+ }
+
+ ESX.updateHUDElement = function(name, data){
+
+ for(let i=0; i' + notif + '');
+
+ $('#inventory_notifications').append(elem);
+
+ $(elem).delay(3000).fadeOut(1000, function(){
+ elem.remove();
+ })
+ }
+
+ window.onData = (data) => {
+
+ switch(data.action){
+
+ case 'setHUDDisplay' : {
+ ESX.setHUDDisplay(data.opacity);
+ break;
+ }
+
+ case 'insertHUDElement' : {
+ ESX.insertHUDElement(data.name, data.index, data.priority, data.html, data.data);
+ break;
+ }
+
+ case 'updateHUDElement' : {
+ ESX.updateHUDElement(data.name, data.data);
+ break;
+ }
+
+ case 'deleteHUDElement' : {
+ ESX.deleteHUDElement(data.name);
+ break;
+ }
+
+ case 'inventoryNotification' : {
+ ESX.inventoryNotification(data.add, data.item, data.count)
+ }
+
+ }
+
+ }
+
+ window.onload = function(e){
+ window.addEventListener('message', (event) => {
+ onData(event.data)
+ });
+ }
+
+})()
\ No newline at end of file
diff --git a/resources/[essential]/es_extended/html/js/mustache.min.js b/resources/[essential]/es_extended/html/js/mustache.min.js
new file mode 100644
index 00000000..520cfcb9
--- /dev/null
+++ b/resources/[essential]/es_extended/html/js/mustache.min.js
@@ -0,0 +1 @@
+(function defineMustache(global,factory){if(typeof exports==="object"&&exports&&typeof exports.nodeName!=="string"){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{global.Mustache={};factory(global.Mustache)}})(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+