955 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			955 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
--// Improved/Custom Native //--
 | 
						|
    StartESX = function(triggerName)
 | 
						|
        ESX = exports["es_extended"]:getSharedObject()
 | 
						|
    end
 | 
						|
    StartQB = function(triggerName)
 | 
						|
        QBCore = exports['qb-core']:GetCoreObject()
 | 
						|
    end
 | 
						|
 | 
						|
    ShowNotification = function(source, msg, type)
 | 
						|
        if GetResourceState("qb-core") == "started" then
 | 
						|
            TriggerClientEvent('QBCore:Notify', source, msg, type)
 | 
						|
        elseif GetResourceState("es_extended") == "started" then
 | 
						|
            TriggerClientEvent('esx:showNotification', source, msg)
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    CreateLoop = function(_function, tickTime)
 | 
						|
        Citizen.CreateThread(function()
 | 
						|
            local active = true
 | 
						|
            _break = function()
 | 
						|
                active = false
 | 
						|
            end
 | 
						|
 | 
						|
            while active do
 | 
						|
                _function()
 | 
						|
                Citizen.Wait(tickTime or 5)
 | 
						|
            end
 | 
						|
        end)
 | 
						|
    end
 | 
						|
 | 
						|
--// Player //--
 | 
						|
    -- Item
 | 
						|
        AddItem = function(source, ...)
 | 
						|
            if ESX then
 | 
						|
                xPlayer = ESX.GetPlayerFromId(source)
 | 
						|
                xPlayer.addInventoryItem(...)
 | 
						|
            else
 | 
						|
                xPlayer = QBCore.Functions.GetPlayer(source)
 | 
						|
                xPlayer.Functions.AddItem(...)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        RemoveItem = function(source, ...)
 | 
						|
            if ESX then
 | 
						|
                xPlayer = ESX.GetPlayerFromId(source)
 | 
						|
                xPlayer.removeInventoryItem(...)
 | 
						|
            else
 | 
						|
                xPlayer = QBCore.Functions.GetPlayer(source)
 | 
						|
                xPlayer.Functions.RemoveItem(...)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        GetItem = function(source, ...)
 | 
						|
            if ESX then
 | 
						|
                xPlayer = ESX.GetPlayerFromId(source)
 | 
						|
                return xPlayer.getInventoryItem(...)
 | 
						|
            else
 | 
						|
                xPlayer = QBCore.Functions.GetPlayer(source)
 | 
						|
                return xPlayer.Functions.GetItemByName(...)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        HaveItem = function(source, ...)
 | 
						|
            local item = GetItem(source, ...)
 | 
						|
            
 | 
						|
            if not item then
 | 
						|
                return false
 | 
						|
            end
 | 
						|
 | 
						|
            if ESX then
 | 
						|
                return item.count > 0
 | 
						|
            else
 | 
						|
                return item.amount > 0
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        HaveItemQuantity = function(source, item, quantity)
 | 
						|
            local item = GetItem(source, item)
 | 
						|
            
 | 
						|
            if not item then
 | 
						|
                return false
 | 
						|
            end
 | 
						|
 | 
						|
            if ESX then
 | 
						|
                return item.count > quantity 
 | 
						|
            else
 | 
						|
                return item.amount > quantity 
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
    -- Money
 | 
						|
        AddMoney = function(source, type, ...)
 | 
						|
            if ESX then
 | 
						|
                xPlayer = ESX.GetPlayerFromId(source)
 | 
						|
                if type == "cash" then
 | 
						|
                    xPlayer.addMoney(...)
 | 
						|
                else
 | 
						|
                    xPlayer.addAccountMoney(type, ...)
 | 
						|
                end
 | 
						|
            else
 | 
						|
                xPlayer = QBCore.Functions.GetPlayer(source)
 | 
						|
                xPlayer.Functions.AddMoney(type, ...)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        RemoveMoney = function(source, type, ...)
 | 
						|
            if ESX then
 | 
						|
                xPlayer = ESX.GetPlayerFromId(source)
 | 
						|
                if type == "cash" then
 | 
						|
                    xPlayer.removeMoney(...)
 | 
						|
                else
 | 
						|
                    xPlayer.removeAccountMoney(type, ...)
 | 
						|
                end
 | 
						|
            else
 | 
						|
                xPlayer = QBCore.Functions.GetPlayer(source)
 | 
						|
                xPlayer.Functions.RemoveMoney(type, ...)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        HaveMoney = function(source, type, amount)
 | 
						|
            if ESX then
 | 
						|
                xPlayer = ESX.GetPlayerFromId(source)
 | 
						|
                if type == "cash" then
 | 
						|
                    return xPlayer.getMoney(type) >= amount
 | 
						|
                else
 | 
						|
                    return xPlayer.getAccount(type).money >= amount
 | 
						|
                end
 | 
						|
            else
 | 
						|
                xPlayer = QBCore.Functions.GetPlayer(source)
 | 
						|
                
 | 
						|
                return xPlayer.Functions.GetMoney(type) >= amount
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
--// MySQL //--
 | 
						|
    StartMySQL = function()
 | 
						|
        MySQL = {
 | 
						|
            Async = {},
 | 
						|
            Sync = {},
 | 
						|
        }
 | 
						|
        
 | 
						|
        local function safeParameters(params)
 | 
						|
            if nil == params then
 | 
						|
                return {[''] = ''}
 | 
						|
            end
 | 
						|
        
 | 
						|
            assert(type(params) == "table", "A table is expected")
 | 
						|
        
 | 
						|
            if next(params) == nil then
 | 
						|
                return {[''] = ''}
 | 
						|
            end
 | 
						|
        
 | 
						|
            return params
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Sync.execute(query, params)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            local res = 0
 | 
						|
            local finishedQuery = false
 | 
						|
            exports['mysql-async']:mysql_execute(query, safeParameters(params), function (result)
 | 
						|
                res = result
 | 
						|
                finishedQuery = true
 | 
						|
            end)
 | 
						|
            repeat Citizen.Wait(0) until finishedQuery == true
 | 
						|
            return res
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Sync.fetchAll(query, params)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            local res = {}
 | 
						|
            local finishedQuery = false
 | 
						|
            exports['mysql-async']:mysql_fetch_all(query, safeParameters(params), function (result)
 | 
						|
                res = result
 | 
						|
                finishedQuery = true
 | 
						|
            end)
 | 
						|
            repeat Citizen.Wait(0) until finishedQuery == true
 | 
						|
            return res
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Sync.fetchScalar(query, params)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            local res = ''
 | 
						|
            local finishedQuery = false
 | 
						|
            exports['mysql-async']:mysql_fetch_scalar(query, safeParameters(params), function (result)
 | 
						|
                res = result
 | 
						|
                finishedQuery = true
 | 
						|
            end)
 | 
						|
            repeat Citizen.Wait(0) until finishedQuery == true
 | 
						|
            return res
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Sync.insert(query, params)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            local res = 0
 | 
						|
            local finishedQuery = false
 | 
						|
            exports['mysql-async']:mysql_insert(query, safeParameters(params), function (result)
 | 
						|
                res = result
 | 
						|
                finishedQuery = true
 | 
						|
            end)
 | 
						|
            repeat Citizen.Wait(0) until finishedQuery == true
 | 
						|
            return res
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Sync.store(query)
 | 
						|
            assert(type(query) == "string", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            local res = -1
 | 
						|
            local finishedQuery = false
 | 
						|
            exports['mysql-async']:mysql_store(query, function (result)
 | 
						|
                res = result
 | 
						|
                finishedQuery = true
 | 
						|
            end)
 | 
						|
            repeat Citizen.Wait(0) until finishedQuery == true
 | 
						|
            return res
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Sync.transaction(querys, params)
 | 
						|
            local res = 0
 | 
						|
            local finishedQuery = false
 | 
						|
            exports['mysql-async']:mysql_transaction(querys, params, function (result)
 | 
						|
                res = result
 | 
						|
                finishedQuery = true
 | 
						|
            end)
 | 
						|
            repeat Citizen.Wait(0) until finishedQuery == true
 | 
						|
            return res
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Async.execute(query, params, func)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            exports['mysql-async']:mysql_execute(query, safeParameters(params), func)
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Async.fetchAll(query, params, func)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            exports['mysql-async']:mysql_fetch_all(query, safeParameters(params), func)
 | 
						|
        end
 | 
						|
 | 
						|
        function MySQL.Async.fetchScalar(query, params, func)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            exports['mysql-async']:mysql_fetch_scalar(query, safeParameters(params), func)
 | 
						|
        end
 | 
						|
        
 | 
						|
        function MySQL.Async.insert(query, params, func)
 | 
						|
            assert(type(query) == "string" or type(query) == "number", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            exports['mysql-async']:mysql_insert(query, safeParameters(params), func)
 | 
						|
        end
 | 
						|
        
 | 
						|
        function MySQL.Async.store(query, func)
 | 
						|
            assert(type(query) == "string", "The SQL Query must be a string")
 | 
						|
        
 | 
						|
            exports['mysql-async']:mysql_store(query, func)
 | 
						|
        end
 | 
						|
        
 | 
						|
        function MySQL.Async.transaction(querys, params, func)
 | 
						|
            return exports['mysql-async']:mysql_transaction(querys, params, func)
 | 
						|
        end
 | 
						|
        
 | 
						|
        function MySQL.ready(callback)
 | 
						|
            Citizen.CreateThread(function ()
 | 
						|
                while GetResourceState('mysql-async') ~= 'started' do
 | 
						|
                    Citizen.Wait(0)
 | 
						|
                end
 | 
						|
                while not exports['mysql-async']:is_ready() do
 | 
						|
                    Citizen.Wait(0)
 | 
						|
                end
 | 
						|
                callback()
 | 
						|
            end)
 | 
						|
        end    
 | 
						|
    end
 | 
						|
 | 
						|
    ExecuteSql = function(query, params)
 | 
						|
        if MySQL == nil then
 | 
						|
            StartMySQL()
 | 
						|
        end
 | 
						|
 | 
						|
        if string.find(query, "SELECT") then
 | 
						|
            return MySQL.Sync.fetchAll(query, params)
 | 
						|
        elseif string.find(query, "INSERT") or string.find(query, "UPDATE") then
 | 
						|
            MySQL.Sync.execute(query, params)
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
--// Society //--
 | 
						|
    -- Item
 | 
						|
        SocietyAddItem = function(society, item, amount)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            TriggerEvent('esx_addoninventory:getSharedInventory', society, function(deposit)
 | 
						|
                deposit.addItem(item, amount)
 | 
						|
            end)
 | 
						|
        end
 | 
						|
 | 
						|
        SocietyRemoveItem = function(society, item, amount)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            TriggerEvent('esx_addoninventory:getSharedInventory', society, function(deposit)
 | 
						|
                deposit.removeItem(item, amount)
 | 
						|
            end)
 | 
						|
        end
 | 
						|
 | 
						|
        SocietyGetItem = function(society, item)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            local _return = nil
 | 
						|
 | 
						|
            TriggerEvent('esx_addoninventory:getSharedInventory', society, function(deposit)
 | 
						|
                _return = deposit.getItem(item)
 | 
						|
            end)
 | 
						|
 | 
						|
            while _return == nil do
 | 
						|
                Citizen.Wait(1)
 | 
						|
            end
 | 
						|
 | 
						|
            return _return
 | 
						|
        end
 | 
						|
 | 
						|
        SocietyHaveItem = function(society, item)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            local _return = nil
 | 
						|
 | 
						|
            TriggerEvent('esx_addoninventory:getSharedInventory', society, function(deposit)
 | 
						|
                local inventoryItem = deposit.getItem(item).count
 | 
						|
                
 | 
						|
                _return = inventoryItem > 0
 | 
						|
            end)
 | 
						|
 | 
						|
            while _return == nil do
 | 
						|
                Citizen.Wait(1)
 | 
						|
            end
 | 
						|
 | 
						|
            return _return
 | 
						|
        end
 | 
						|
 | 
						|
        SocietyHaveItemQuantity = function(society, item, quantity)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            local _return = nil
 | 
						|
 | 
						|
            TriggerEvent('esx_addoninventory:getSharedInventory', society, function(deposit)
 | 
						|
                local inventoryItem = deposit.getItem(item).count
 | 
						|
                
 | 
						|
                _return = inventoryItem > quantity
 | 
						|
            end)
 | 
						|
 | 
						|
            while _return == nil do
 | 
						|
                Citizen.Wait(1)
 | 
						|
            end
 | 
						|
 | 
						|
            return _return
 | 
						|
        end
 | 
						|
 | 
						|
    -- Money
 | 
						|
        SocietyAddMoney = function(society, amount)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            TriggerEvent('esx_addonaccount:getSharedAccount', society, function(account)
 | 
						|
                account.addMoney(amount)
 | 
						|
            end)
 | 
						|
        end
 | 
						|
 | 
						|
        SocietyRemoveMoney = function(society, amount)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            TriggerEvent('esx_addonaccount:getSharedAccount', society, function(account)
 | 
						|
                account.removeMoney(amount)
 | 
						|
            end)
 | 
						|
        end
 | 
						|
 | 
						|
        SocietyHaveMoney = function(society, amount)
 | 
						|
            if not string.find(society, "society_") then
 | 
						|
                society = "society_"..society
 | 
						|
            end
 | 
						|
 | 
						|
            local have = nil
 | 
						|
            TriggerEvent('esx_addonaccount:getSharedAccount', society, function(account)
 | 
						|
                have = account.money >= amount
 | 
						|
            end)
 | 
						|
 | 
						|
            while have == nil do
 | 
						|
                Citizen.Wait(1)
 | 
						|
            end
 | 
						|
 | 
						|
            return have
 | 
						|
        end
 | 
						|
 | 
						|
--// Misc //--
 | 
						|
    printd = function(_table, advanced)
 | 
						|
        if advanced then
 | 
						|
            local printTable_cache = {}
 | 
						|
 | 
						|
            local function sub_printTable(t, indent)
 | 
						|
                if (printTable_cache[tostring(t)]) then
 | 
						|
                    print(indent.."*"..tostring(t))
 | 
						|
                else
 | 
						|
                    printTable_cache[tostring(t)] = true
 | 
						|
                    if (type(t) == "table") then
 | 
						|
                        for pos,val in pairs(t) do
 | 
						|
                            if (type(val) == "table") then
 | 
						|
                                print(indent.."["..pos.."] => "..tostring(t).. " {" )
 | 
						|
                                    sub_printTable(val, indent..string.rep(" ", string.len(pos)+8))
 | 
						|
                                print(indent..string.rep(" ", string.len(pos)+6 ).."}")
 | 
						|
                            elseif (type(val) == "string") then
 | 
						|
                                print(indent.."["..pos.."] => \"" .. val .. "\"")
 | 
						|
                            else
 | 
						|
                                print(indent.."["..pos.."] => "..tostring(val))
 | 
						|
                            end
 | 
						|
                        end
 | 
						|
                    else
 | 
						|
                        print(indent..tostring(t))
 | 
						|
                    end
 | 
						|
                end
 | 
						|
            end
 | 
						|
        
 | 
						|
            if (type(_table) == "table") then
 | 
						|
                print(tostring(_table).." {")
 | 
						|
                sub_printTable(_table, "  ")
 | 
						|
                print("}")
 | 
						|
            else
 | 
						|
                developer("^1Error^0", "error dumping table ".._table.." why isnt a table", "")
 | 
						|
            end
 | 
						|
        else
 | 
						|
            if type(_table) == "table" then
 | 
						|
                print(json.encode(_table, {indent = true}))
 | 
						|
            else
 | 
						|
                developer("^1Error^0", "error dumping table ".._table.." why isnt a table", "")
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    local string_gsub = string.gsub
 | 
						|
    string.multigsub = function(string, table, new)
 | 
						|
        if type(table) then
 | 
						|
            for i=1, #table do
 | 
						|
                string = string_gsub(string, table[i], new[i])
 | 
						|
            end
 | 
						|
        else
 | 
						|
            for i=1, #table do
 | 
						|
                string = string_gsub(string, table[i], new)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        return string
 | 
						|
    end
 | 
						|
 | 
						|
 | 
						|
    table.fexist = function(_table, field)
 | 
						|
        return _table[field] ~= nil
 | 
						|
    end
 | 
						|
 | 
						|
    local table_remove = table.remove
 | 
						|
    table.remove = function(_table, index, onlyfirst)
 | 
						|
        if type(index) == "number" then
 | 
						|
            return table_remove(_table, index)
 | 
						|
        elseif type(index) == "string" then
 | 
						|
            for k, v in pairs(_table) do
 | 
						|
                if k == index then
 | 
						|
                    _table[k] = nil -- Can be bugged, probably in future update will be changed with a empty table
 | 
						|
 | 
						|
                    if onlyfirst then
 | 
						|
                        return k
 | 
						|
                    end
 | 
						|
                end
 | 
						|
            end
 | 
						|
        else
 | 
						|
            return table_remove(_table)
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    ---Check if a table is empty.
 | 
						|
    ---@param t table
 | 
						|
    ---@return boolean
 | 
						|
    table.empty = function(t)
 | 
						|
        return next(t) == nil
 | 
						|
    end
 | 
						|
 | 
						|
    ---Internal usage: Inserts a value into a table at a given key, or appends to the end if the key is a number.
 | 
						|
    ---@param t table
 | 
						|
    ---@param k any
 | 
						|
    ---@param v any
 | 
						|
    local table_insert = function(t, k, v)
 | 
						|
        if type(k) == "number" then
 | 
						|
            table.insert(t, v)
 | 
						|
        else
 | 
						|
            t[k] = v
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    ---Merges two tables together, if the same key is found in both tables the second table takes precedence.
 | 
						|
    ---@param t1 table
 | 
						|
    ---@param t2 table
 | 
						|
    ---@return table
 | 
						|
    table.merge = function(t1, t2)
 | 
						|
        ---@type table
 | 
						|
        local result = table.clone(t1)
 | 
						|
 | 
						|
        for k, v in pairs(t2) do
 | 
						|
            table_insert(result, k, v)
 | 
						|
        end
 | 
						|
 | 
						|
        return result
 | 
						|
    end
 | 
						|
 | 
						|
 | 
						|
    ---Checks if the given value exists in the table, if a function is given it test it on each value until it returns true.
 | 
						|
    ---@param t table
 | 
						|
    ---@param value any|fun(value: any): boolean
 | 
						|
    ---@return boolean
 | 
						|
    table.includes = function(t, value)
 | 
						|
        if type(value) == "function" then
 | 
						|
            for _, v in pairs(t) do
 | 
						|
                if value(v) then
 | 
						|
                    return true
 | 
						|
                end
 | 
						|
            end
 | 
						|
        else
 | 
						|
            for _, v in pairs(t) do
 | 
						|
                if value == v then
 | 
						|
                    return true
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        return false
 | 
						|
    end
 | 
						|
 | 
						|
    ---Filters a table using a given filter, which can be an another table or a function.
 | 
						|
    ---@param t table
 | 
						|
    ---@param filter table|fun(k: any, v: any): boolean
 | 
						|
    ---@return table
 | 
						|
    table.filter = function(t, filter)
 | 
						|
        local result = {}
 | 
						|
 | 
						|
        if type(filter) == "function" then
 | 
						|
            -- returns true.
 | 
						|
            for k, v in pairs(t) do
 | 
						|
                if filter(k, v) then
 | 
						|
                    table_insert(result, k, v)
 | 
						|
                end
 | 
						|
            end
 | 
						|
        elseif type(filter) == "table" then
 | 
						|
            for k, v in pairs(t) do
 | 
						|
                if table.includes(filter, v) then
 | 
						|
                    table_insert(result, k, v)
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        return result
 | 
						|
    end
 | 
						|
 | 
						|
    ---Searches a table for the given value and returns the key and value if found.
 | 
						|
    ---@param t table
 | 
						|
    ---@param value any|fun(value: any): boolean
 | 
						|
    ---@return any
 | 
						|
    table.find = function(t, value)
 | 
						|
        if type(value) == "function" then
 | 
						|
            for k, v in pairs(t) do
 | 
						|
                if value(v) then
 | 
						|
                    return k, v
 | 
						|
                end
 | 
						|
            end
 | 
						|
        else
 | 
						|
            for k, v in pairs(t) do
 | 
						|
                if value == v then
 | 
						|
                    return k, v
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    ---Returns a table with all keys of the given table.
 | 
						|
    ---@param t table
 | 
						|
    ---@return table
 | 
						|
    table.keys = function(t)
 | 
						|
        local keys = {}    
 | 
						|
        for k, _ in pairs(t) do
 | 
						|
            table.insert(keys, k)
 | 
						|
        end
 | 
						|
 | 
						|
        return keys
 | 
						|
    end
 | 
						|
 | 
						|
    ---Returns a table with all values of the given table.
 | 
						|
    ---@param t table
 | 
						|
    ---@return table
 | 
						|
    table.values = function(t)
 | 
						|
        local values = {}
 | 
						|
        
 | 
						|
        for _, v in pairs(t) do
 | 
						|
            table.insert(values, v)
 | 
						|
        end
 | 
						|
        
 | 
						|
        return values
 | 
						|
    end
 | 
						|
 | 
						|
    math.round = function(number, decimals)
 | 
						|
        local _ = 10 ^ decimals
 | 
						|
        return math.floor((number * _) + 0.5) / (_)
 | 
						|
    end
 | 
						|
 | 
						|
    -- https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/
 | 
						|
    math.lerp = function(start, _end, perc)
 | 
						|
        return start + (_end - start) * perc
 | 
						|
    end
 | 
						|
 | 
						|
    math.invlerp = function(start, _end, value)
 | 
						|
        return (value - start) / (_end - start)
 | 
						|
    end
 | 
						|
 | 
						|
    GetDataForJob = function(job)
 | 
						|
        return exports["utility_lib"]:GetDataForJob(job)
 | 
						|
    end
 | 
						|
 | 
						|
    quat2euler = function(q)
 | 
						|
        -- roll (x-axis rotation)
 | 
						|
        local sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
 | 
						|
        local cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
 | 
						|
        local roll = math.atan2(sinr_cosp, cosr_cosp);
 | 
						|
    
 | 
						|
        -- pitch (y-axis rotation)
 | 
						|
        local sinp = math.sqrt(1 + 2 * (q.w * q.y - q.x * q.z));
 | 
						|
        local cosp = math.sqrt(1 - 2 * (q.w * q.y - q.x * q.z));
 | 
						|
        local pitch = 2 * math.atan2(sinp, cosp) - math.pi / 2;
 | 
						|
    
 | 
						|
        -- yaw (z-axis rotation)
 | 
						|
        local siny_cosp = 2 * (q.w * q.z + q.x * q.y);
 | 
						|
        local cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
 | 
						|
        local yaw = math.atan2(siny_cosp, cosy_cosp);
 | 
						|
    
 | 
						|
        return vec3(math.deg(roll), math.deg(pitch), math.deg(yaw));
 | 
						|
    end
 | 
						|
 | 
						|
    GenerateMatrix = function(pos, rot)
 | 
						|
        local rx, ry, rz = math.rad(rot.x), math.rad(rot.y), math.rad(rot.z)
 | 
						|
    
 | 
						|
        -- Precompute
 | 
						|
        local cosX, sinX = math.cos(rx), math.sin(rx)
 | 
						|
        local cosY, sinY = math.cos(ry), math.sin(ry)
 | 
						|
        local cosZ, sinZ = math.cos(rz), math.sin(rz)
 | 
						|
    
 | 
						|
        local mrx = mat3(
 | 
						|
            vec3(1, 0, 0),
 | 
						|
            vec3(0, cosX, -sinX),
 | 
						|
            vec3(0, sinX, cosX)
 | 
						|
        )
 | 
						|
        
 | 
						|
        local mry = mat3(
 | 
						|
            vec3(cosY, 0, sinY),
 | 
						|
            vec3(0, 1, 0),
 | 
						|
            vec3(-sinY, 0, cosY)
 | 
						|
        )
 | 
						|
    
 | 
						|
        local mrz = mat3(
 | 
						|
            vec3(cosZ, -sinZ, 0),
 | 
						|
            vec3(sinZ, cosZ, 0),
 | 
						|
            vec3(0, 0, 1)
 | 
						|
        )
 | 
						|
    
 | 
						|
        local rotationMatrix = mrx * mry * mrz
 | 
						|
    
 | 
						|
        -- Construct the final transform matrix
 | 
						|
        local transformMatrix = mat4(
 | 
						|
            vec4(rotationMatrix[1].x, rotationMatrix[2].x, rotationMatrix[3].x, 0),
 | 
						|
            vec4(rotationMatrix[1].y, rotationMatrix[2].y, rotationMatrix[3].y, 0),
 | 
						|
            vec4(rotationMatrix[1].z, rotationMatrix[2].z, rotationMatrix[3].z, 0),
 | 
						|
            vec4(pos.x, pos.y, pos.z, 1)
 | 
						|
        )
 | 
						|
    
 | 
						|
        return transformMatrix
 | 
						|
    end
 | 
						|
    
 | 
						|
    GetOffsetFromPositionInWorldCoords = function(pos, rot, offset)
 | 
						|
        local m = GenerateMatrix(pos, rot)
 | 
						|
        return m * offset
 | 
						|
    end
 | 
						|
--// Slices //--
 | 
						|
    local sliceSize = 100.0
 | 
						|
    local slicesLength = 8100
 | 
						|
    local sliceCollumns = slicesLength / sliceSize
 | 
						|
 | 
						|
    function GetSliceColRowFromCoords(coords)
 | 
						|
        local row = math.floor((coords.x) / sliceSize)
 | 
						|
        local col = math.floor((coords.y) / sliceSize)
 | 
						|
 | 
						|
        return col, row
 | 
						|
    end
 | 
						|
 | 
						|
    function GetWorldCoordsFromSlice(slice)
 | 
						|
        local col = math.floor(slice / sliceCollumns)
 | 
						|
        local row = slice % sliceCollumns
 | 
						|
 | 
						|
        return vec3((row * sliceSize), (col * sliceSize), 0.0)
 | 
						|
    end
 | 
						|
 | 
						|
    function GetSliceIdFromColRow(col, row)
 | 
						|
        return math.floor(col * sliceCollumns + row)
 | 
						|
    end
 | 
						|
 | 
						|
    function GetSliceFromCoords(pos)
 | 
						|
        local col, row = GetSliceColRowFromCoords(pos)
 | 
						|
 | 
						|
        return GetSliceIdFromColRow(col, row)
 | 
						|
    end
 | 
						|
 | 
						|
    function GetSurroundingSlices(slice)
 | 
						|
        local top = slice - sliceCollumns
 | 
						|
        local bottom = slice + sliceCollumns
 | 
						|
 | 
						|
        local right = slice - 1
 | 
						|
        local left = slice + 1
 | 
						|
 | 
						|
        local topright = slice - sliceCollumns - 1
 | 
						|
        local topleft = slice - sliceCollumns + 1
 | 
						|
        local bottomright = slice + sliceCollumns - 1
 | 
						|
        local bottomleft = slice + sliceCollumns + 1
 | 
						|
 | 
						|
        return {top, bottom, left, right, topright, topleft, bottomright, bottomleft}
 | 
						|
    end
 | 
						|
 | 
						|
--// UtilityNet //--
 | 
						|
local CreatedEntities = {}
 | 
						|
UtilityNet = {}
 | 
						|
 | 
						|
UtilityNet.ForEachEntity = function(fn, slice)
 | 
						|
    if slice then
 | 
						|
        if not GlobalState.Entities[slice] then
 | 
						|
            return
 | 
						|
        end
 | 
						|
 | 
						|
        for k,v in pairs(GlobalState.Entities[slice]) do
 | 
						|
            local ret = fn(v, k)
 | 
						|
 | 
						|
            if ret ~= nil then
 | 
						|
                return ret
 | 
						|
            end
 | 
						|
        end
 | 
						|
    else
 | 
						|
        if not GlobalState.Entities then
 | 
						|
            return
 | 
						|
        end
 | 
						|
 | 
						|
        for sliceI,slice in pairs(GlobalState.Entities) do
 | 
						|
            for k2, v in pairs(slice) do
 | 
						|
                local ret = fn(v, k2)
 | 
						|
 | 
						|
                if ret ~= nil then
 | 
						|
                    return ret
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.CreateEntity = function(model, coords, options)
 | 
						|
    local id = exports["utility_lib"]:CreateEntity(model, coords, options)
 | 
						|
    table.insert(CreatedEntities, id)
 | 
						|
 | 
						|
    return id
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.SetEntityModel = function(uNetId, model)
 | 
						|
    return exports["utility_lib"]:SetEntityModel(uNetId, model)
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.DeleteEntity = function(uNetId)
 | 
						|
    for k, v in pairs(CreatedEntities) do
 | 
						|
        if v == uNetId then
 | 
						|
            table.remove(CreatedEntities, k)
 | 
						|
            break
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    return exports["utility_lib"]:DeleteEntity(uNetId)
 | 
						|
end
 | 
						|
 | 
						|
-- Returns the slice the entity is in
 | 
						|
UtilityNet.InternalFindFromNetId = function(uNetId)
 | 
						|
    for sliceI, slice in pairs(GlobalState.Entities) do
 | 
						|
        if slice[uNetId] then
 | 
						|
            return slice[uNetId], sliceI
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.DoesUNetIdExist = function(uNetId)
 | 
						|
    local entity = UtilityNet.InternalFindFromNetId(uNetId)
 | 
						|
 | 
						|
    return entity or false
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.GetEntityCoords = function(uNetId)
 | 
						|
    local entity = UtilityNet.InternalFindFromNetId(uNetId)
 | 
						|
 | 
						|
    if entity then
 | 
						|
        return entity.coords
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.GetEntityRotation = function(uNetId)
 | 
						|
    local entity = UtilityNet.InternalFindFromNetId(uNetId)
 | 
						|
 | 
						|
    if entity then
 | 
						|
        return entity.options.rotation
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.GetEntityModel = function(uNetId)
 | 
						|
    local entity = UtilityNet.InternalFindFromNetId(uNetId)
 | 
						|
 | 
						|
    if entity then
 | 
						|
        return entity.model
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.SetModelRenderDistance = function(model, distance)
 | 
						|
    return exports["utility_lib"]:SetModelRenderDistance(model, distance)
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.SetEntityCoords = function(uNetId, newCoords)
 | 
						|
    return exports["utility_lib"]:SetEntityCoords(uNetId, newCoords)
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.SetEntityRotation = function(uNetId, newRotation)
 | 
						|
    return exports["utility_lib"]:SetEntityRotation(uNetId, newRotation)
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.DetachEntity = function(uNetId)
 | 
						|
    TriggerEvent("Utility:Net:DetachEntity", uNetId)
 | 
						|
end
 | 
						|
 | 
						|
local getValueAsStateTable = nil
 | 
						|
getValueAsStateTable = function(id, baseKey, depth)
 | 
						|
    depth = depth or {}
 | 
						|
 | 
						|
    local getCurrentTable = function()
 | 
						|
        local baseTable = exports["utility_lib"]:GetEntityStateValue(id, baseKey)
 | 
						|
 | 
						|
        -- Dive into table
 | 
						|
        for k,v in pairs(depth) do
 | 
						|
            baseTable = baseTable[v]
 | 
						|
        end
 | 
						|
 | 
						|
        return baseTable
 | 
						|
    end
 | 
						|
 | 
						|
    return setmetatable({
 | 
						|
        __internal_statetable = true,
 | 
						|
        raw = function(self)
 | 
						|
            return getCurrentTable()
 | 
						|
        end
 | 
						|
    }, {
 | 
						|
        -- Iterators
 | 
						|
        __pairs = function(self)
 | 
						|
            return pairs(getCurrentTable())
 | 
						|
        end,
 | 
						|
 | 
						|
        __ipairs = function(self)
 | 
						|
            return ipairs(getCurrentTable())
 | 
						|
        end,
 | 
						|
 | 
						|
        __len = function(self)
 | 
						|
            return #getCurrentTable()
 | 
						|
        end,
 | 
						|
 | 
						|
        __index = function(_, k)
 | 
						|
            local currentTable = getCurrentTable()
 | 
						|
 | 
						|
            if type(currentTable[k]) == "table" then
 | 
						|
                -- Clone the table to dont mess up the current depth table
 | 
						|
                local clonedDepth = table.clone(depth)
 | 
						|
                table.insert(clonedDepth, k)
 | 
						|
 | 
						|
                -- Generate another state table but more in depth
 | 
						|
                return getValueAsStateTable(id, baseKey, clonedDepth)
 | 
						|
            else
 | 
						|
                return currentTable[k]
 | 
						|
            end
 | 
						|
        end,
 | 
						|
        __newindex = function(_, k, v)
 | 
						|
            local baseTable = exports["utility_lib"]:GetEntityStateValue(id, baseKey)
 | 
						|
            local currentTable = baseTable
 | 
						|
 | 
						|
            -- Dive into table
 | 
						|
            for k,v in pairs(depth) do
 | 
						|
                currentTable = currentTable[v]
 | 
						|
            end
 | 
						|
    
 | 
						|
            -- Set
 | 
						|
            currentTable[k] = v
 | 
						|
            
 | 
						|
            -- Update state table
 | 
						|
            exports["utility_lib"]:SetEntityStateValue(id, baseKey, baseTable)
 | 
						|
        end
 | 
						|
    })
 | 
						|
end
 | 
						|
 | 
						|
UtilityNet.State = function(id)
 | 
						|
    local state = setmetatable({
 | 
						|
        raw = function(self)
 | 
						|
            return exports["utility_lib"]:GetEntityStateValue(id)
 | 
						|
        end
 | 
						|
    }, {
 | 
						|
        __index = function(_, k)
 | 
						|
            local value = exports["utility_lib"]:GetEntityStateValue(id, k)
 | 
						|
 | 
						|
            if type(value) == "table" then
 | 
						|
                return getValueAsStateTable(id, k, {})
 | 
						|
            else
 | 
						|
                return value
 | 
						|
            end
 | 
						|
        end,
 | 
						|
 | 
						|
        __newindex = function(_, k, v)
 | 
						|
            -- If the value is a state table get the raw table (without metatable)
 | 
						|
            if type(v) == "table" and v.__internal_statetable then
 | 
						|
                v = v:raw()
 | 
						|
            end
 | 
						|
 | 
						|
            exports["utility_lib"]:SetEntityStateValue(id, k, v)
 | 
						|
        end
 | 
						|
    })
 | 
						|
 | 
						|
    return state
 | 
						|
end
 | 
						|
 | 
						|
AddEventHandler("onResourceStop", function(resourceName)
 | 
						|
    if resourceName == GetCurrentResourceName() then
 | 
						|
        for k, v in pairs(CreatedEntities) do
 | 
						|
            exports["utility_lib"]:DeleteEntity(v)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end)
 |