3339 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			3339 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
_G["xPlayer"], _G["source"], _G["developer"] = {}, GetPlayerServerId(PlayerId()), function() end
 | 
						||
 | 
						||
-- Why?, see that https://www.lua.org/gems/sample.pdf#page=3
 | 
						||
local _AddTextEntry, _BeginTextCommandDisplayHelp, _EndTextCommandDisplayHelp, _SetNotificationTextEntry, _AddTextComponentSubstringPlayerName, _DrawNotification, _GetEntityCoords, _World3dToScreen2d, _SetTextScale, _SetTextFont, _SetTextEntry, _SetTextCentre, _AddTextComponentString, _DrawText, _DoesEntityExist, _GetDistanceBetweenCoords, _GetPlayerPed, _TriggerEvent, _TriggerServerEvent = AddTextEntry, BeginTextCommandDisplayHelp, EndTextCommandDisplayHelp, SetNotificationTextEntry, AddTextComponentSubstringPlayerName, DrawNotification, GetEntityCoords, World3dToScreen2d, SetTextScale, SetTextFont, SetTextEntry, SetTextCentre, AddTextComponentString, DrawText, DoesEntityExist, GetDistanceBetweenCoords, GetPlayerPed, TriggerEvent, TriggerServerEvent
 | 
						||
 | 
						||
local resName = GetCurrentResourceName()
 | 
						||
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
 | 
						||
}
 | 
						||
 | 
						||
DevModeStatus = false
 | 
						||
UtilityLibLoaded = true
 | 
						||
 | 
						||
local Utility = {
 | 
						||
    Cache = {
 | 
						||
        PlayerPedId = PlayerPedId(),
 | 
						||
        Marker = {},
 | 
						||
        Object = {},
 | 
						||
        Dialogue = {},
 | 
						||
        Blips = {},
 | 
						||
        N3d = {},
 | 
						||
        Events = {},
 | 
						||
 | 
						||
        Guards = {},
 | 
						||
        Scenes = {},
 | 
						||
        
 | 
						||
        SetData = {},
 | 
						||
        Frozen = {},
 | 
						||
        FlowDetector = {},
 | 
						||
 | 
						||
        Textures = {},
 | 
						||
        --Constant = {},
 | 
						||
	Settings = {},
 | 
						||
        EntityStack = {},
 | 
						||
        Loop = {},
 | 
						||
        SliceGroups = {}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
UtilityNet = {}
 | 
						||
 | 
						||
Citizen.CreateThreadNow(function() -- Load Classes
 | 
						||
    if UFAPI then -- if the utility framework API is loaded
 | 
						||
        if resName == "utility_lib" then
 | 
						||
            _G["Utility"] = Utility
 | 
						||
        else
 | 
						||
            _G["UtilityLibrary"] = Utility
 | 
						||
        end
 | 
						||
    else -- the utility framework API is not loaded :(
 | 
						||
        _G["Utility"] = Utility
 | 
						||
    end
 | 
						||
end)
 | 
						||
 | 
						||
UseDelete = function(boolean)
 | 
						||
    Utility.Cache.Settings.UseDelete = boolean
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
--// Emitter //--
 | 
						||
    On = function(type, function_id, fake_triggerable)
 | 
						||
        RegisterNetEvent("Utility:On:".. (fake_triggerable and "!" or "") ..type)
 | 
						||
 | 
						||
        local event = AddEventHandler("Utility:On:".. (fake_triggerable and "!" or "") ..type, function_id)
 | 
						||
        table.insert(Utility.Cache.Events, event)
 | 
						||
 | 
						||
        return event
 | 
						||
    end
 | 
						||
 | 
						||
--// Custom/Improved Native //-- 
 | 
						||
    _G.old_TaskVehicleDriveToCoord = TaskVehicleDriveToCoord
 | 
						||
    TaskVehicleDriveToCoord = function(ped, vehicle, destination, speed, stopRange)
 | 
						||
        old_TaskVehicleDriveToCoord(ped, vehicle, destination, speed or 10.0, 0, GetEntityModel(vehicle), 2883621, stopRange or 1.0)
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_DisableControlAction = DisableControlAction
 | 
						||
    DisableControlAction = function(group, control, disable)
 | 
						||
        disable = disable ~= nil and disable or true
 | 
						||
 | 
						||
        if Keys[string.upper(group)] then
 | 
						||
            return old_DisableControlAction(0, Keys[string.upper(group)], control)
 | 
						||
        else
 | 
						||
            return old_DisableControlAction(group, control, disable) -- Retro compatibility
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    DisableControlForSeconds = function(control, seconds)
 | 
						||
        local sec = seconds
 | 
						||
 | 
						||
        Citizen.CreateThread(function()
 | 
						||
            while sec > 0 do
 | 
						||
                Citizen.Wait(1000)
 | 
						||
                sec = sec - 1
 | 
						||
            end
 | 
						||
            return
 | 
						||
        end)
 | 
						||
 | 
						||
        Citizen.CreateThread(function()
 | 
						||
            while sec > 0 do
 | 
						||
                DisableControlAction(Keys[string.upper(control)])
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
            return
 | 
						||
        end)
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_IsControlJustPressed = IsControlJustPressed
 | 
						||
    IsControlJustPressed = function(key, _function, description)
 | 
						||
        if type(key) == "number" then
 | 
						||
            local inputGroup = key
 | 
						||
            local control = _function
 | 
						||
 | 
						||
            return old_IsControlJustPressed(inputGroup, control)
 | 
						||
        end
 | 
						||
 | 
						||
        developer("^2Created^0", "key map", key)
 | 
						||
        local input = "keyboard"
 | 
						||
        key = key:lower()
 | 
						||
 | 
						||
        if key:find("mouse_") or key:find("iom_wheel") then
 | 
						||
            input = "mouse_button"
 | 
						||
        elseif key:find("_index") then
 | 
						||
            input = "pad_digitalbutton"
 | 
						||
        elseif key:find("iom_axis") then
 | 
						||
            input = "pad_axis"
 | 
						||
        end
 | 
						||
 | 
						||
        RegisterKeyMapping('utility '..resName..' '..key, (description or ''), input, key)
 | 
						||
 | 
						||
        local eventHandler = nil
 | 
						||
 | 
						||
        Citizen.CreateThread(function()
 | 
						||
            Citizen.Wait(500)
 | 
						||
            eventHandler = RegisterNetEvent("Utility:Pressed_"..resName.."_"..key, _function)
 | 
						||
 | 
						||
            table.insert(Utility.Cache.Events, eventHandler)
 | 
						||
        end)
 | 
						||
    end
 | 
						||
 | 
						||
    ShowNotification = function(msg)
 | 
						||
        _SetNotificationTextEntry('STRING')
 | 
						||
        _AddTextComponentSubstringPlayerName(msg)
 | 
						||
        _DrawNotification(false, true)
 | 
						||
    end
 | 
						||
 | 
						||
    ButtonNotification = function(msg)
 | 
						||
        if string.match(msg, "{.*}") then
 | 
						||
            msg = string.multigsub(msg, {"{A}","{B}", "{C}", "{D}", "{E}", "{F}", "{G}", "{H}", "{L}", "{M}", "{N}", "{O}", "{P}", "{Q}", "{R}", "{S}", "{T}", "{U}", "{V}", "{W}", "{X}", "{Y}", "{Z}"}, {"~INPUT_VEH_FLY_YAW_LEFT~", "~INPUT_SPECIAL_ABILITY_SECONDARY~", "~INPUT_LOOK_BEHIND~", "~INPUT_MOVE_LR~", "~INPUT_CONTEXT~", "~INPUT_ARREST~", "~INPUT_DETONATE~", "~INPUT_VEH_ROOF~", "~INPUT_CELLPHONE_CAMERA_FOCUS_LOCK~", "~INPUT_INTERACTION_MENU~", "~INPUT_REPLAY_ENDPOINT~" , "~INPUT_FRONTEND_PAUSE~", "~INPUT_FRONTEND_LB~", "~INPUT_RELOAD~", "~INPUT_MOVE_DOWN_ONLY~", "~INPUT_MP_TEXT_CHAT_ALL~", "~INPUT_REPLAY_SCREENSHOT~", "~INPUT_NEXT_CAMERA~", "~INPUT_MOVE_UP_ONLY~", "~INPUT_VEH_HOTWIRE_LEFT~", "~INPUT_VEH_DUCK~", "~INPUT_MP_TEXT_CHAT_TEAM~", "~INPUT_HUD_SPECIAL~"})
 | 
						||
        end
 | 
						||
            
 | 
						||
        _AddTextEntry('ButtonNotification'..string.len(msg), msg)
 | 
						||
        _BeginTextCommandDisplayHelp('ButtonNotification'..string.len(msg))
 | 
						||
        _EndTextCommandDisplayHelp(0, false, true, -1)
 | 
						||
    end
 | 
						||
 | 
						||
    ButtonFor = function(msg, ms)
 | 
						||
        local timer = GetGameTimer()
 | 
						||
    
 | 
						||
        Citizen.CreateThread(function()
 | 
						||
            while (GetGameTimer() - timer) < (ms or 5000) do
 | 
						||
                ButtonNotification(msg)
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
        end)
 | 
						||
    end
 | 
						||
 | 
						||
    FloatingNotification = function(msg, coords)
 | 
						||
        _AddTextEntry('FloatingNotification', msg)
 | 
						||
        SetFloatingHelpTextWorldPosition(1, coords)
 | 
						||
        SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0)
 | 
						||
        _BeginTextCommandDisplayHelp('FloatingNotification')
 | 
						||
        _EndTextCommandDisplayHelp(2, false, false, -1)
 | 
						||
    end
 | 
						||
 | 
						||
    MakeEntityFaceEntity = function(entity1, entity2, whatentity)
 | 
						||
        local coords1 = _GetEntityCoords(entity1, true)
 | 
						||
        local coords2 = _GetEntityCoords(entity2, true)
 | 
						||
 | 
						||
        if whatentity then
 | 
						||
            local heading = GetHeadingFromVector_2d(coords2.x - coords1.x, coords2.y - coords1.y)
 | 
						||
            SetEntityHeading(entity1, heading)
 | 
						||
        else
 | 
						||
            local heading = GetHeadingFromVector_2d(coords1.x - coords2.x, coords1.y - coords2.y)
 | 
						||
            SetEntityHeading(entity2, heading)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    DrawText3Ds = function(coords, text, scale, font, rectangle)
 | 
						||
        if coords then
 | 
						||
            local onScreen, _x, _y = _World3dToScreen2d(coords.x, coords.y, coords.z)
 | 
						||
 | 
						||
            if onScreen then
 | 
						||
                _SetTextScale(scale or 0.35, scale or 0.35)
 | 
						||
                _SetTextFont(font or 4)
 | 
						||
                _SetTextEntry("STRING")
 | 
						||
                _SetTextCentre(1)
 | 
						||
 | 
						||
                _AddTextComponentString(text)
 | 
						||
                _DrawText(_x, _y)
 | 
						||
 | 
						||
                if rectangle then
 | 
						||
                local factor = (string.len(text))/370
 | 
						||
                local _, count = string.gsub(factor, "\n", "\n") * 0.025
 | 
						||
                if count == nil then count = 0 end
 | 
						||
 | 
						||
                DrawRect(_x, _y + 0.0125, 0.025 + factor, 0.025 + count, 0, 0, 0, 90)
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_TaskPlayAnim = TaskPlayAnim
 | 
						||
    TaskPlayAnim = function(ped, animDictionary, ...)
 | 
						||
        if not HasAnimDictLoaded(animDictionary) then
 | 
						||
            RequestAnimDict(animDictionary)
 | 
						||
            while not HasAnimDictLoaded(animDictionary) do Citizen.Wait(1) end
 | 
						||
        end
 | 
						||
 | 
						||
        old_TaskPlayAnim(ped, animDictionary, ...)
 | 
						||
        RemoveAnimDict(animDictionary)
 | 
						||
    end
 | 
						||
 | 
						||
    TaskEasyPlayAnim = function(dict, anim, move, duration)
 | 
						||
        if move == nil then move = 51 end
 | 
						||
        if duration == nil then duration = -1 end
 | 
						||
 | 
						||
        TaskPlayAnim(PlayerPedId(), dict, anim, 2.0, 2.0, duration, move, 0)
 | 
						||
 | 
						||
        if duration > -1 or duration > 0 then
 | 
						||
            Citizen.Wait(duration)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_CreateObject = CreateObject
 | 
						||
    CreateObject = function(model, ...)
 | 
						||
        local modelHash = model
 | 
						||
 | 
						||
        if type(model) == "string" then
 | 
						||
            modelHash = GetHashKey(model)
 | 
						||
        end
 | 
						||
 | 
						||
        if not IsModelValid(modelHash) then
 | 
						||
            error("Model \""..model.."\" loaded from \""..GetCurrentResourceName().."\" is not valid^0")
 | 
						||
            return
 | 
						||
        end
 | 
						||
 | 
						||
        if not HasModelLoaded(modelHash) then
 | 
						||
            RequestModel(modelHash);
 | 
						||
            while not HasModelLoaded(modelHash) do Citizen.Wait(1); end  
 | 
						||
        end
 | 
						||
 | 
						||
        local obj = old_CreateObject(modelHash, ...)
 | 
						||
 | 
						||
	    if Utility.Cache.Settings.UseDelete then
 | 
						||
            table.insert(Utility.Cache.EntityStack, obj)
 | 
						||
        end	
 | 
						||
 | 
						||
        SetModelAsNoLongerNeeded(modelHash) 
 | 
						||
 | 
						||
        local netId = 0
 | 
						||
 | 
						||
        if NetworkGetEntityIsNetworked(obj) then
 | 
						||
            netId = ObjToNet(obj)
 | 
						||
            SetNetworkIdExistsOnAllMachines(netId, true)
 | 
						||
            SetNetworkIdCanMigrate(netId, true)
 | 
						||
        end
 | 
						||
 | 
						||
        return obj, netId
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_CreatePed = CreatePed
 | 
						||
    CreatePed = function(modelHash, ...)
 | 
						||
        if type(modelHash) == "string" then
 | 
						||
            modelHash = GetHashKey(modelHash)
 | 
						||
        end
 | 
						||
 | 
						||
        if not HasModelLoaded(modelHash) then
 | 
						||
            RequestModel(modelHash);
 | 
						||
            while not HasModelLoaded(modelHash) do Citizen.Wait(1); end
 | 
						||
        end  
 | 
						||
 | 
						||
        local ped = old_CreatePed(0, modelHash, ...)
 | 
						||
        SetModelAsNoLongerNeeded(modelHash) 
 | 
						||
 | 
						||
        local netId = 0
 | 
						||
 | 
						||
        if NetworkGetEntityIsNetworked(ped) then
 | 
						||
            netId = PedToNet(ped)
 | 
						||
            SetNetworkIdExistsOnAllMachines(netId, true)
 | 
						||
            SetNetworkIdCanMigrate(netId, true)
 | 
						||
        end
 | 
						||
 | 
						||
        return ped, netId
 | 
						||
    end
 | 
						||
 | 
						||
    SetPedStatic = function(entity, active)
 | 
						||
        FreezeEntityPosition(entity, active)
 | 
						||
        SetEntityInvincible(entity, active)
 | 
						||
        SetBlockingOfNonTemporaryEvents(entity, active)
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_CreateVehicle = CreateVehicle
 | 
						||
    CreateVehicle = function(modelHash, ...)
 | 
						||
        if type(modelHash) == "string" then
 | 
						||
            modelHash = GetHashKey(modelHash)
 | 
						||
        end
 | 
						||
 | 
						||
        if not HasModelLoaded(modelHash) then
 | 
						||
            RequestModel(modelHash);
 | 
						||
            while not HasModelLoaded(modelHash) do Citizen.Wait(1); end
 | 
						||
        end  
 | 
						||
 | 
						||
        local veh = old_CreateVehicle(modelHash, ...)
 | 
						||
        SetModelAsNoLongerNeeded(modelHash) 
 | 
						||
 | 
						||
        local netId = 0
 | 
						||
 | 
						||
        if NetworkGetEntityIsNetworked(veh) then
 | 
						||
            netId = VehToNet(veh)
 | 
						||
            SetNetworkIdExistsOnAllMachines(netId, true)
 | 
						||
            SetNetworkIdCanMigrate(netId, true)
 | 
						||
        end
 | 
						||
 | 
						||
        return veh, netId
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_DeleteEntity = DeleteEntity
 | 
						||
    DeleteEntity = function(entity, isnetwork)
 | 
						||
        if not isnetwork then
 | 
						||
            local attempts = 0
 | 
						||
            NetworkRequestControlOfEntity(entity)
 | 
						||
            -- entity = entityHandler
 | 
						||
            while not NetworkRequestControlOfEntity(entity) and attempts < 10 do
 | 
						||
                attempts = attempts + 1
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            if not IsEntityAMissionEntity(entity) then
 | 
						||
                SetEntityAsMissionEntity(entity)
 | 
						||
            end
 | 
						||
 | 
						||
            old_DeleteEntity(entity)
 | 
						||
        else
 | 
						||
            -- entity = networkID
 | 
						||
            NetworkRequestControlOfNetworkId(entity)
 | 
						||
            
 | 
						||
            local new_entity = NetworkGetEntityFromNetworkId(entity)
 | 
						||
            local attempts = 0
 | 
						||
 | 
						||
            while not NetworkRequestControlOfEntity(new_entity) and attempts < 10 do
 | 
						||
                attempts = attempts + 1
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            SetEntityAsMissionEntity(new_entity)
 | 
						||
            old_DeleteEntity(new_entity)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_GetPlayerName = GetPlayerName
 | 
						||
    GetPlayerName = function(id)
 | 
						||
        return old_GetPlayerName(id or PlayerId())
 | 
						||
    end
 | 
						||
 | 
						||
    _G.old_PlayerPedId = PlayerPedId
 | 
						||
    PlayerPedId = function()
 | 
						||
        if not _DoesEntityExist(Utility.Cache.PlayerPedId) then Utility.Cache.PlayerPedId = old_PlayerPedId() end
 | 
						||
        return Utility.Cache.PlayerPedId
 | 
						||
    end
 | 
						||
 | 
						||
 -- Loop
 | 
						||
    -- Before _break
 | 
						||
    StopLoop = function(id)
 | 
						||
        Utility.Cache.Loop[id].status = false
 | 
						||
    end
 | 
						||
 | 
						||
 | 
						||
    CreateLoop = function(_function, tickTime, dontstart)
 | 
						||
        local loopId = RandomId(5)
 | 
						||
 | 
						||
        Utility.Cache.Loop[loopId] = {
 | 
						||
            status = true,
 | 
						||
            func = _function,
 | 
						||
            tick = tickTime
 | 
						||
        }
 | 
						||
 | 
						||
        if dontstart ~= false then
 | 
						||
            Citizen.CreateThread(function()
 | 
						||
                while Utility.Cache.Loop[loopId] and Utility.Cache.Loop[loopId].status do
 | 
						||
                    _function(loopId)
 | 
						||
                    Citizen.Wait(tickTime or 1)
 | 
						||
                end
 | 
						||
            end)
 | 
						||
        end
 | 
						||
 | 
						||
        return loopId
 | 
						||
    end
 | 
						||
 | 
						||
    PauseLoop = function(loopId, delay)
 | 
						||
        Citizen.SetTimeout(delay or 0, function()
 | 
						||
            print("Pausing loop "..loopId)
 | 
						||
            Utility.Cache.Loop[loopId].status = false
 | 
						||
        end)
 | 
						||
    end
 | 
						||
 | 
						||
    ResumeLoop = function(loopId, delay)
 | 
						||
        local current = Utility.Cache.Loop[loopId]
 | 
						||
        
 | 
						||
        Citizen.SetTimeout(delay or 0, function()
 | 
						||
            print("Resuming loop "..loopId)
 | 
						||
            current.status = true
 | 
						||
            Citizen.CreateThread(function()
 | 
						||
                while current and current.status do
 | 
						||
                    current.func(loopId)
 | 
						||
                    Citizen.Wait(current.tick or 1)
 | 
						||
                end
 | 
						||
            end)
 | 
						||
        end)
 | 
						||
    end
 | 
						||
 | 
						||
    GetWorldClosestPed = function(radius)
 | 
						||
        local closest = 0
 | 
						||
        local AllFoundedPed = GetGamePool("CPed")
 | 
						||
        local coords = _GetEntityCoords(PlayerPedId())
 | 
						||
        local minDistance = radius + 5.0
 | 
						||
 | 
						||
        for i=1, #AllFoundedPed do
 | 
						||
            local distance = _GetDistanceBetweenCoords(coords, _GetEntityCoords(AllFoundedPed[i]), false)
 | 
						||
 | 
						||
            if distance <= radius and AllFoundedPed[i] ~= PlayerPedId() then
 | 
						||
                if minDistance > distance then
 | 
						||
                    minDistance = distance
 | 
						||
                    closest = AllFoundedPed[i]
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        return closest, AllFoundedPed
 | 
						||
    end
 | 
						||
 | 
						||
    GetWorldClosestPlayer = function(radius)
 | 
						||
        local closest = 0
 | 
						||
        local AllPlayers = {}
 | 
						||
        local minDistance = radius + 5.0
 | 
						||
 | 
						||
        local AllFoundedPed = GetGamePool("CPed")
 | 
						||
        
 | 
						||
        local coords = _GetEntityCoords(PlayerPedId())
 | 
						||
 | 
						||
        for i=1, #AllFoundedPed do
 | 
						||
            if IsPedAPlayer(AllFoundedPed[i]) then
 | 
						||
                table.insert(AllPlayers, NetworkGetPlayerIndexFromPed(AllFoundedPed[i]))
 | 
						||
 | 
						||
                local distance = _GetDistanceBetweenCoords(coords, _GetEntityCoords(AllFoundedPed[i]), false)
 | 
						||
 | 
						||
                if distance <= radius and AllFoundedPed[i] ~= PlayerPedId() then
 | 
						||
                    if minDistance > distance then
 | 
						||
                        minDistance = distance
 | 
						||
                        closest = NetworkGetPlayerIndexFromPed(AllFoundedPed[i])
 | 
						||
                    end
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        return closest, AllPlayers
 | 
						||
    end
 | 
						||
 | 
						||
    GetEntitySurfaceMaterial = function(entity)
 | 
						||
        local coords = GetEntityCoords(entity)
 | 
						||
 | 
						||
        local shape_result = StartShapeTestCapsule(coords.x,coords.y,coords.z,coords.x,coords.y,coords.z-2.5, 2, 1, entity, 7)
 | 
						||
        local _, hitted, _, _, materialHash = GetShapeTestResultIncludingMaterial(shape_result)
 | 
						||
 | 
						||
        return materialHash, hitted
 | 
						||
    end
 | 
						||
 | 
						||
    GetLoadoutOfPed = function(ped)
 | 
						||
        local list = ESX.GetWeaponList()
 | 
						||
        local loadout = {}
 | 
						||
 | 
						||
        for i=1, #list, 1 do
 | 
						||
            local hash = GetHashKey(list.name)
 | 
						||
 | 
						||
            if HasPedGotWeapon(ped, hash, false) then
 | 
						||
                table.insert(loadout, {name = list.name, hash = hash, ammo = GetAmmoInPedWeapon(ped, hash)})
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        return loadout
 | 
						||
    end
 | 
						||
 | 
						||
    local old_FreezeEntityPosition = FreezeEntityPosition
 | 
						||
    FreezeEntityPosition = function(entity, active)
 | 
						||
        Utility.Cache.Frozen[entity] = active
 | 
						||
        old_FreezeEntityPosition(entity, active)
 | 
						||
    end
 | 
						||
 | 
						||
    IsEntityFrozen = function(entity)
 | 
						||
        return Utility.Cache.Frozen[entity] == true
 | 
						||
    end
 | 
						||
 | 
						||
    GetNearestValue = function(v, all_v)
 | 
						||
        local diff = 100 * 100000000000
 | 
						||
        local _i = 0
 | 
						||
    
 | 
						||
        for i=1, #all_v do
 | 
						||
            local c_diff = math.abs(all_v[i] - v)
 | 
						||
    
 | 
						||
            if (c_diff < diff) then
 | 
						||
                diff = c_diff
 | 
						||
                n = all_v[i]
 | 
						||
                _i = i
 | 
						||
            end
 | 
						||
        end
 | 
						||
    
 | 
						||
        return n, diff, _i
 | 
						||
    end
 | 
						||
 | 
						||
--// Frameworks integration //--
 | 
						||
    -- Init
 | 
						||
        StartESX = function(eventName, second_job)
 | 
						||
            Citizen.CreateThreadNow(function()
 | 
						||
                ESX = exports["es_extended"]:getSharedObject()
 | 
						||
                
 | 
						||
                while ESX.GetPlayerData().job == nil do
 | 
						||
                    Citizen.Wait(1)
 | 
						||
                end
 | 
						||
                
 | 
						||
                uPlayer = ESX.GetPlayerData()
 | 
						||
                xPlayer = uPlayer
 | 
						||
                
 | 
						||
                if second_job ~= nil then
 | 
						||
                    while ESX.GetPlayerData()[second_job] == nil do
 | 
						||
                        Citizen.Wait(1)
 | 
						||
                    end
 | 
						||
 | 
						||
                    uPlayer = ESX.GetPlayerData()
 | 
						||
                    xPlayer = uPlayer
 | 
						||
                end
 | 
						||
 | 
						||
                if second_job ~= nil then
 | 
						||
                    RegisterNetEvent('esx:set'..string.upper(second_job:sub(1,1))..second_job:sub(2), function(job)        
 | 
						||
                        uPlayer[second_job] = job
 | 
						||
                        xPlayer = uPlayer
 | 
						||
                    end)
 | 
						||
                end
 | 
						||
            
 | 
						||
                RegisterNetEvent('esx:setJob', function(job)        
 | 
						||
                    uPlayer.job = job
 | 
						||
                    xPlayer = uPlayer
 | 
						||
                
 | 
						||
                    if OnJobUpdate then
 | 
						||
                        OnJobUpdate()
 | 
						||
                    end
 | 
						||
                end)
 | 
						||
            end)
 | 
						||
        end 
 | 
						||
        StartQB = function()
 | 
						||
            QBCore = exports['qb-core']:GetCoreObject()
 | 
						||
            uPlayer = QBCore.Functions.GetPlayerData()
 | 
						||
            Player = uPlayer
 | 
						||
 | 
						||
            RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
 | 
						||
                uPlayer = QBCore.Functions.GetPlayerData()
 | 
						||
                Player = uPlayer
 | 
						||
            end)
 | 
						||
 | 
						||
            RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo)
 | 
						||
                uPlayer.job = JobInfo
 | 
						||
                Player = uPlayer
 | 
						||
 | 
						||
                if OnJobUpdate then
 | 
						||
                    OnJobUpdate()
 | 
						||
                end
 | 
						||
            end)
 | 
						||
        end
 | 
						||
        StartFramework = function()
 | 
						||
            if GetResourceState("qb-core") ~= "missing" then
 | 
						||
                StartQB()
 | 
						||
 | 
						||
                return true
 | 
						||
            elseif GetResourceState("es_extended") ~= "missing" then
 | 
						||
                StartESX()
 | 
						||
 | 
						||
                return true
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
    -- Job
 | 
						||
        GetDataForJob = function(job)
 | 
						||
            local job_info = promise:new()
 | 
						||
 | 
						||
            if GetResourceState("es_extended") == "started" then
 | 
						||
                ESX.TriggerServerCallback("Utility:GetJobData", function(worker)
 | 
						||
                    job_info:resolve(worker)
 | 
						||
                end, job)    
 | 
						||
            elseif GetResourceState("qb-core") == "started" then
 | 
						||
                QBCore.Functions.TriggerCallback("Utility:GetJobData", function(worker)
 | 
						||
                    job_info:resolve(worker)
 | 
						||
                end, job)    
 | 
						||
            end
 | 
						||
 | 
						||
            job_info = Citizen.Await(job_info)
 | 
						||
            return #job_info, job_info
 | 
						||
        end
 | 
						||
 | 
						||
--// Advanced script creation //--
 | 
						||
    local _GetOnHandObject = 0
 | 
						||
 | 
						||
    GetOnHandObject = function()
 | 
						||
        return _GetOnHandObject
 | 
						||
    end
 | 
						||
 | 
						||
    TakeObjectOnHand = function(ped, entityToGrab, zOffset, xPos, yPos, zPos, xRot, yRot, zRot)
 | 
						||
        developer("^2Taking^0", "object", entityToGrab.." ("..GetEntityModel(entityToGrab)..")")
 | 
						||
 | 
						||
        if type(entityToGrab) == "number" then -- Send an entity ID (Use already exist entity)
 | 
						||
	        NetworkRequestControlOfEntity(entityToGrab)
 | 
						||
            while not NetworkHasControlOfEntity(entityToGrab) do Citizen.Wait(1) end
 | 
						||
		
 | 
						||
            TaskPlayAnim(ped, "anim@heists@box_carry@", "idle", 3.0, 3.0, -1, 63, 0, 0, 0, 0)
 | 
						||
            Citizen.Wait(100)
 | 
						||
            AttachEntityToEntity(entityToGrab, ped, GetPedBoneIndex(ped, 60309), xPos or 0.2, yPos or 0.08, zPos or 0.2, xRot or -45.0, yRot or 290.0, zRot or 0.0, true, true, false, true, 1, true)
 | 
						||
 | 
						||
            _GetOnHandObject = entityToGrab
 | 
						||
        elseif type(entityToGrab) == "string" then -- Send a model name (New object created)
 | 
						||
            local coords = _GetEntityCoords(ped)      
 | 
						||
            local prop = CreateObject(entityToGrab, coords + vector3(0.0, 0.0, zOffset or 0.2), true, false, false)
 | 
						||
            
 | 
						||
            SetEntityAsMissionEntity(prop)
 | 
						||
            TaskPlayAnim(ped, "anim@heists@box_carry@", "idle", 3.0, -8, -1, 63, 0, 0, 0, 0)
 | 
						||
            Citizen.Wait(100)
 | 
						||
            AttachEntityToEntity(prop, ped, GetPedBoneIndex(ped, 60309), xPos or 0.2, yPos or 0.08, zPos or 0.2, xRot or -45.0, yRot or 290.0, zRot or 0.0, true, true, false, true, 1, true)
 | 
						||
 | 
						||
            _GetOnHandObject = prop
 | 
						||
            return prop
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    DropObjectFromHand = function(entityToDrop, delete)
 | 
						||
        if delete then
 | 
						||
            developer("^1Deleting^0","from hand", entityToDrop)
 | 
						||
 | 
						||
            DeleteEntity(entityToDrop)
 | 
						||
        else
 | 
						||
            developer("^3Dont delete^0","from hand", entityToDrop)
 | 
						||
 | 
						||
            DetachEntity(entityToDrop)
 | 
						||
            SetEntityCoords(entityToDrop, GetOffsetFromEntityInWorldCoords(entityToDrop, 0, 0.5, 0))
 | 
						||
            PlaceObjectOnGroundProperly(entityToDrop)
 | 
						||
            FreezeEntityPosition(entityToDrop, true)
 | 
						||
        end
 | 
						||
 | 
						||
        ClearPedTasks(PlayerPedId())
 | 
						||
        _GetOnHandObject = 0
 | 
						||
    end
 | 
						||
 | 
						||
    IsInRadius = function(coords1, coords2, radius, debugSphere)
 | 
						||
        local distance = #(coords1-coords2)
 | 
						||
 | 
						||
        if debugSphere then
 | 
						||
            DrawSphere(coords2, radius, 255, 0, 0, 0.5)
 | 
						||
        end
 | 
						||
        return distance < radius
 | 
						||
    end
 | 
						||
 | 
						||
    IsNearCoords = function(coords, radius, debugSphere)
 | 
						||
        local distance = #(GetEntityCoords(PlayerPedId())-coords)
 | 
						||
 | 
						||
        if debugSphere then
 | 
						||
            DrawSphere(coords, radius, 255, 0, 0, 0.5)
 | 
						||
        end
 | 
						||
        return distance < radius
 | 
						||
    end
 | 
						||
    
 | 
						||
    GenerateRandomCoords = function(coords, radius, heading)
 | 
						||
        local x = coords.x + math.random(-radius, radius)
 | 
						||
        local y = coords.y + math.random(-radius, radius)
 | 
						||
        local _, z = GetGroundZFor_3dCoord(x,y,200.0,0)
 | 
						||
 | 
						||
        if heading then
 | 
						||
            return vector3(x,y,z), math.random(0.0, 360.0)
 | 
						||
        end
 | 
						||
 | 
						||
        return vector3(x,y,z)
 | 
						||
    end
 | 
						||
 | 
						||
--// Managing data (like table, but more easy to use) //--
 | 
						||
 | 
						||
    SetFor = function(id, property, value)
 | 
						||
        -- If id dont already exist register it for store data
 | 
						||
        if Utility.Cache["SetData"][id] == nil then
 | 
						||
            Utility.Cache["SetData"][id] = {}
 | 
						||
        end
 | 
						||
 | 
						||
        if type(property) == "table" then -- Table
 | 
						||
            for k,v in pairs(property) do
 | 
						||
                if type(v) == "function" then
 | 
						||
                    developer("^2Setting^0", "data", "("..id..") ["..k.." = "..tostring(v).."] {table}")
 | 
						||
                else
 | 
						||
                    developer("^2Setting^0", "data", "("..id..") ["..k.." = "..json.encode(v).."] {table}")
 | 
						||
                end
 | 
						||
                Utility.Cache["SetData"][id][k] = v
 | 
						||
            end
 | 
						||
        else -- Single
 | 
						||
            if type(value) == "function" then
 | 
						||
                developer("^2Setting^0", "data", "("..id..") ["..property.." = "..tostring(value).."] {single}")
 | 
						||
            else
 | 
						||
                developer("^2Setting^0", "data", "("..id..") ["..property.." = "..json.encode(value).."] {single}")
 | 
						||
            end
 | 
						||
            Utility.Cache["SetData"][id][property] = value
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    GetFrom = function(id, property)
 | 
						||
        if property == nil then
 | 
						||
            property = "not defined"
 | 
						||
        end
 | 
						||
 | 
						||
        developer("^3Getting^0", "data", "("..id..") ["..property.."]")
 | 
						||
 | 
						||
        if Utility.Cache["SetData"][id] ~= nil then
 | 
						||
            if property == "not defined" then
 | 
						||
                return Utility.Cache["SetData"][id]
 | 
						||
            else
 | 
						||
                return Utility.Cache["SetData"][id][property]
 | 
						||
            end
 | 
						||
        else
 | 
						||
            return nil
 | 
						||
        end
 | 
						||
    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 GetEntitySlice(ped)
 | 
						||
        return GetSliceFromCoords(GetEntityCoords(ped))
 | 
						||
    end
 | 
						||
 | 
						||
    function GetPlayerSlice(player)
 | 
						||
        local ped = GetPlayerPed(player)
 | 
						||
 | 
						||
        return GetSliceFromCoords(GetEntityCoords(ped))
 | 
						||
    end
 | 
						||
    
 | 
						||
    function GetSelfSlice()
 | 
						||
        local ped = PlayerPedId()
 | 
						||
 | 
						||
        return GetSliceFromCoords(GetEntityCoords(ped))
 | 
						||
    end
 | 
						||
 | 
						||
    function IsOnScreen(coords)
 | 
						||
        local onScreen, _x, _y = World3dToScreen2d(coords.x, coords.y, coords.z)
 | 
						||
                        
 | 
						||
        return onScreen
 | 
						||
    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
 | 
						||
 | 
						||
    function DrawDebugSlice(slice, drawSorroundings, zOffset)
 | 
						||
        local drawRects = {}
 | 
						||
        local sorrounding = drawSorroundings and GetSurroundingSlices(slice) or {}
 | 
						||
    
 | 
						||
        for k,v in pairs({slice, table.unpack(sorrounding)}) do
 | 
						||
            local bottomLeftSlice = slice + sliceCollumns + 1
 | 
						||
    
 | 
						||
            table.insert(drawRects, {
 | 
						||
                GetWorldCoordsFromSlice(v)               + vec3(0.0,0.0, zOffset or 500.0),
 | 
						||
                GetWorldCoordsFromSlice(bottomLeftSlice) + vec3(0.0,0.0, zOffset or 500.0),
 | 
						||
            })
 | 
						||
        end
 | 
						||
    
 | 
						||
        -- Predefined colors to distinguish slices
 | 
						||
        local colors = {
 | 
						||
            {255, 0, 0},
 | 
						||
            {0, 255, 0},
 | 
						||
            {0, 0, 255},
 | 
						||
            {255, 255, 0},
 | 
						||
            {0, 255, 255},
 | 
						||
            {255, 0, 255},
 | 
						||
            {255, 255, 255}
 | 
						||
        }
 | 
						||
 | 
						||
        -- Draw rects
 | 
						||
        for i=1, #drawRects do
 | 
						||
            local color = colors[i % #colors + 1]
 | 
						||
 | 
						||
            DrawBox(drawRects[i][1], drawRects[i][2], color[1], color[2], color[3], 150)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    function SliceUsed(slice)
 | 
						||
        return Utility.Cache.SliceGroups[slice] or false
 | 
						||
    end
 | 
						||
    
 | 
						||
    function SetSliceUsed(slice, value)
 | 
						||
        slice = tonumber(slice)
 | 
						||
 | 
						||
        if value then
 | 
						||
            Utility.Cache.SliceGroups[slice] = value
 | 
						||
        else
 | 
						||
            Utility.Cache.SliceGroups[slice] = nil
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
 | 
						||
--// Marker/Object/Blip //--
 | 
						||
    -- Marker
 | 
						||
    RandomId = function(length)
 | 
						||
        length = length or 5
 | 
						||
 | 
						||
        local maxvalue = ""
 | 
						||
 | 
						||
        for i=1, length do
 | 
						||
            maxvalue = maxvalue.."9"
 | 
						||
        end
 | 
						||
 | 
						||
        return math.random(0, maxvalue)
 | 
						||
    end
 | 
						||
 | 
						||
    CreateMarker = function(id, coords, render_distance, interaction_distance, options)
 | 
						||
        if DoesExist("m", id) then
 | 
						||
            Citizen.Wait(100)
 | 
						||
            return
 | 
						||
        else
 | 
						||
            if type(coords) ~= "vector3" then
 | 
						||
                developer("^1Error^0","You can use only vector3 for coords!",id)
 | 
						||
                return
 | 
						||
            end
 | 
						||
 | 
						||
            id = string.gsub(id, "{r}", RandomId())
 | 
						||
 | 
						||
            developer("^2Created^0","Marker",id)
 | 
						||
 | 
						||
            local _marker = {
 | 
						||
                render_distance = render_distance,
 | 
						||
                interaction_distance = interaction_distance,
 | 
						||
                coords = coords,
 | 
						||
                slice = (options and options.slice == "ignore") and "ignore" or GetSliceFromCoords(coords)
 | 
						||
            }
 | 
						||
 | 
						||
            -- Options
 | 
						||
 | 
						||
            if type(options) == "table" then
 | 
						||
                if options.rgb ~= nil then -- Marker
 | 
						||
                    _marker.type = 1
 | 
						||
                    _marker.rgb = options.rgb
 | 
						||
                elseif options.text ~= nil then -- 3d Text
 | 
						||
                    _marker.type = 0
 | 
						||
                    _marker.text = options.text
 | 
						||
                else
 | 
						||
                    _marker.type = 1
 | 
						||
                    _marker.rgb = {options[1], options[2], options[3]}
 | 
						||
                end
 | 
						||
                
 | 
						||
                if options.type ~= nil and type(options.type) == "number" then _marker._type = options.type end
 | 
						||
                if options.direction ~= nil and type(options.direction) == "vector3" then _marker._direction = options.direction end
 | 
						||
                if options.rotation ~= nil and type(options.rotation) == "vector3" then _marker._rot = options.rotation end
 | 
						||
                if options.scale ~= nil and type(options.scale) == "vector3" then _marker._scale = options.scale end
 | 
						||
                if options.alpha ~= nil and type(options.alpha) == "number" then _marker.alpha = options.alpha end
 | 
						||
                if options.animation ~= nil and type(options.animation) == "boolean" then _marker.anim = options.animation end
 | 
						||
                if options.job ~= nil then _marker.job = options.job end
 | 
						||
 | 
						||
                if options.notify ~= nil then
 | 
						||
                    local notify = string.multigsub(options.notify, {"{A}","{B}", "{C}", "{D}", "{E}", "{F}", "{G}", "{H}", "{L}", "{M}", "{N}", "{O}", "{P}", "{Q}", "{R}", "{S}", "{T}", "{U}", "{V}", "{W}", "{X}", "{Y}", "{Z}"}, {"~INPUT_VEH_FLY_YAW_LEFT~", "~INPUT_SPECIAL_ABILITY_SECONDARY~", "~INPUT_LOOK_BEHIND~", "~INPUT_MOVE_LR~", "~INPUT_CONTEXT~", "~INPUT_ARREST~", "~INPUT_DETONATE~", "~INPUT_VEH_ROOF~", "~INPUT_CELLPHONE_CAMERA_FOCUS_LOCK~", "~INPUT_INTERACTION_MENU~", "~INPUT_REPLAY_ENDPOINT~" , "~INPUT_FRONTEND_PAUSE~", "~INPUT_FRONTEND_LB~", "~INPUT_RELOAD~", "~INPUT_MOVE_DOWN_ONLY~", "~INPUT_MP_TEXT_CHAT_ALL~", "~INPUT_REPLAY_SCREENSHOT~", "~INPUT_NEXT_CAMERA~", "~INPUT_MOVE_UP_ONLY~", "~INPUT_VEH_HOTWIRE_LEFT~", "~INPUT_VEH_DUCK~", "~INPUT_MP_TEXT_CHAT_TEAM~", "~INPUT_HUD_SPECIAL~"})
 | 
						||
                    _marker.notify = notify
 | 
						||
                end
 | 
						||
            elseif type(options) == "string" then
 | 
						||
                _marker.type = 0
 | 
						||
                _marker.text = options
 | 
						||
            end
 | 
						||
            
 | 
						||
            Utility.Cache.Marker[id] = _marker -- Sync the local table
 | 
						||
            _TriggerEvent("Utility:Create", "Marker", id, _marker) -- Sync the table in the utility_lib
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
        SetMarker = function(id, _type, key, value)
 | 
						||
            if (type(value) ~= _type) and (value ~= nil) then
 | 
						||
                developer("^1Error^0", key.." can be only a ".._type, "[Marker]")
 | 
						||
                return 
 | 
						||
            end
 | 
						||
            
 | 
						||
            if DoesExist("marker", id) then
 | 
						||
                Utility.Cache.Marker[id][key] = value
 | 
						||
                _TriggerEvent("Utility:Edit", "Marker", id, key, value)
 | 
						||
            else
 | 
						||
                developer("^1Error^0", "Unable to edit the marker as it does not exist", id)
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerType = function(id, _type)
 | 
						||
            SetMarker(id, "number", "_type", _type)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerDirection = function(id, direction)
 | 
						||
            SetMarker(id, "vector3", "_direction", direction)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerRotation = function(id, rot)
 | 
						||
            SetMarker(id, "vector3", "_rot", rot)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerScale = function(id, scale)
 | 
						||
            SetMarker(id, "vector3", "_scale", scale)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerColor = function(id, rgb)
 | 
						||
            SetMarker(id, "table", "rgb", rgb)
 | 
						||
        end
 | 
						||
 | 
						||
        ---
 | 
						||
        SetMarkerCoords = function(id, coords)
 | 
						||
            SetMarker(id, "string", "slice", tostring(GetSliceFromCoords(coords)))
 | 
						||
            SetMarker(id, "vector3", "coords", coords)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerRenderDistance = function(id, render_distance)
 | 
						||
            SetMarker(id, "number", "render_distance", render_distance)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerInteractionDistance = function(id, interaction_distance)
 | 
						||
            SetMarker(id, "number", "interaction_distance", interaction_distance)
 | 
						||
        end
 | 
						||
        ---
 | 
						||
 | 
						||
        SetMarkerAlpha = function(id, alpha)
 | 
						||
            SetMarker(id, "number", "alpha", alpha)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerAnimation = function(id, active)
 | 
						||
            SetMarker(id, "boolean", "anim", active)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerDrawOnEntity = function(id, active)
 | 
						||
            SetMarker(id, "boolean", "draw_entity", active)
 | 
						||
        end
 | 
						||
 | 
						||
        SetMarkerNotify = function(id, text)
 | 
						||
            if type(text) == "string" then
 | 
						||
                text = string.multigsub(text, {"{A}","{B}", "{C}", "{D}", "{E}", "{F}", "{G}", "{H}", "{L}", "{M}", "{N}", "{O}", "{P}", "{Q}", "{R}", "{S}", "{T}", "{U}", "{V}", "{W}", "{X}", "{Y}", "{Z}"}, {"~INPUT_VEH_FLY_YAW_LEFT~", "~INPUT_SPECIAL_ABILITY_SECONDARY~", "~INPUT_LOOK_BEHIND~", "~INPUT_MOVE_LR~", "~INPUT_PICKUP~", "~INPUT_ARREST~", "~INPUT_DETONATE~", "~INPUT_VEH_ROOF~", "~INPUT_CELLPHONE_CAMERA_FOCUS_LOCK~", "~INPUT_INTERACTION_MENU~", "~INPUT_REPLAY_ENDPOINT~" , "~INPUT_FRONTEND_PAUSE~", "~INPUT_FRONTEND_LB~", "~INPUT_RELOAD~", "~INPUT_MOVE_DOWN_ONLY~", "~INPUT_MP_TEXT_CHAT_ALL~", "~INPUT_REPLAY_SCREENSHOT~", "~INPUT_NEXT_CAMERA~", "~INPUT_MOVE_UP_ONLY~", "~INPUT_VEH_HOTWIRE_LEFT~", "~INPUT_VEH_DUCK~", "~INPUT_MP_TEXT_CHAT_TEAM~", "~INPUT_HUD_SPECIAL~"})
 | 
						||
            end
 | 
						||
 | 
						||
            SetMarker(id, "string", "notify", text)
 | 
						||
        end
 | 
						||
 | 
						||
        -- 3dText
 | 
						||
        Set3dText = function(id, text)
 | 
						||
            SetMarker(id, "string", "text", text)
 | 
						||
        end
 | 
						||
 | 
						||
        Set3dTextScale = function(id, scale)
 | 
						||
            SetMarker(id, "number", "_scale", scale)
 | 
						||
        end
 | 
						||
 | 
						||
        Set3dTextDrawRect = function(id, active)
 | 
						||
            SetMarker(id, "boolean", "rect", active)
 | 
						||
        end
 | 
						||
 | 
						||
        Set3dTextFont = function(id, font)
 | 
						||
            SetMarker(id, "number", "font", font)
 | 
						||
        end
 | 
						||
 | 
						||
    DeleteMarker = function(id)
 | 
						||
        if not DoesExist("m", id) then
 | 
						||
            Citizen.Wait(100)
 | 
						||
            return
 | 
						||
        else
 | 
						||
            developer("^1Deleted^0","Marker",id)
 | 
						||
            Utility.Cache.Marker[id] = nil
 | 
						||
            _TriggerEvent("Utility:Remove", "Marker", id)
 | 
						||
            ClearAllHelpMessages()
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    -- Object
 | 
						||
    CreateiObject = function(id, model, pos, heading, interaction_distance, network, job)
 | 
						||
        developer("^2Created^0 Object "..id.." ("..model..")")
 | 
						||
 | 
						||
        local obj
 | 
						||
        if network ~= nil then
 | 
						||
            obj = CreateObject(GetHashKey(model), pos.x,pos.y,pos.z, network, false, false) or nil
 | 
						||
        else
 | 
						||
            obj = CreateObject(GetHashKey(model), pos.x,pos.y,pos.z, true, false, false) or nil
 | 
						||
        end
 | 
						||
 | 
						||
        SetEntityHeading(obj, heading)
 | 
						||
        SetEntityAsMissionEntity(obj, true, true)
 | 
						||
        FreezeEntityPosition(obj, true)
 | 
						||
        SetModelAsNoLongerNeeded(hash)
 | 
						||
 | 
						||
        _object = {
 | 
						||
            obj = obj,
 | 
						||
            coords = pos,
 | 
						||
            interaction_distance = interaction_distance or 3.0,
 | 
						||
            slice = GetSliceFromCoords(pos),
 | 
						||
            job = job
 | 
						||
        }
 | 
						||
 | 
						||
        Utility.Cache.Object[id] = _object -- Sync the local table
 | 
						||
        _TriggerEvent("Utility:Create", "Object", id, _object) -- Sync the table in the utility_lib
 | 
						||
        return obj, _GetEntityCoords(obj)
 | 
						||
    end
 | 
						||
 | 
						||
    DeleteiObject = function(id, delete)
 | 
						||
        developer("^1Deleted^0","Object",id)
 | 
						||
 | 
						||
        if delete then
 | 
						||
            DeleteEntity(Utility.Cache.Object[id].obj)
 | 
						||
        end
 | 
						||
 | 
						||
        Utility.Cache.Object[id] = nil
 | 
						||
        _TriggerEvent("Utility:Remove", "Object", id)
 | 
						||
    end
 | 
						||
 | 
						||
    -- Blip
 | 
						||
    CreateBlip = function(name, coords, sprite, colour, scale)
 | 
						||
        developer("^2Created^0","Blip",name)
 | 
						||
 | 
						||
        local blip = AddBlipForCoord(coords)
 | 
						||
 | 
						||
        SetBlipSprite (blip, sprite)
 | 
						||
        SetBlipScale  (blip, scale or 1.0)
 | 
						||
        SetBlipColour (blip, colour)
 | 
						||
        SetBlipAsShortRange(blip, true)
 | 
						||
 | 
						||
        BeginTextCommandSetBlipName('STRING')
 | 
						||
        _AddTextComponentSubstringPlayerName(name)
 | 
						||
        EndTextCommandSetBlipName(blip)
 | 
						||
        return blip
 | 
						||
    end
 | 
						||
 | 
						||
    CreateJobBlip = function(name, coords, job, sprite, colour, scale)
 | 
						||
        _TriggerEvent("Utility:Create", "Blips", math.random(10000, 99999), {
 | 
						||
            name = name,
 | 
						||
            coords = coords,
 | 
						||
            job = job,
 | 
						||
            sprite = sprite,
 | 
						||
            colour = colour,
 | 
						||
            scale = scale or 1.0
 | 
						||
        })
 | 
						||
    end
 | 
						||
 | 
						||
    -- Get/Edit
 | 
						||
    SetIdOf = function(type, id, new_id)
 | 
						||
        if type:lower() == "marker" or type:lower() == "m" then
 | 
						||
            type = "Marker"
 | 
						||
        elseif type:lower() == "object" or type:lower() == "o" then
 | 
						||
            type = "Object"
 | 
						||
        else
 | 
						||
            return nil
 | 
						||
        end
 | 
						||
        
 | 
						||
        if DoesExist(type, id) then
 | 
						||
            Utility.Cache[type][new_id] = Utility.Cache[type][id]
 | 
						||
 | 
						||
            Utility.Cache[type][id] = nil
 | 
						||
        else
 | 
						||
            developer("^1Error^0", "Unable to set id of the "..type.." as it does not exist", id)
 | 
						||
            return
 | 
						||
        end
 | 
						||
 | 
						||
        developer("^3Change^0", "Setted id to "..new_id.." of the id", id)
 | 
						||
 | 
						||
        _TriggerEvent("Utility:Remove", type, id)
 | 
						||
        _TriggerEvent("Utility:Create", type, new_id, Utility.Cache[type][new_id]) -- Sync the table in the utility_lib
 | 
						||
    end
 | 
						||
 | 
						||
    GetDistanceFrom = function(type, id)
 | 
						||
        if type:lower() == "marker" or type:lower() == "m" then
 | 
						||
            type = "Marker"
 | 
						||
        elseif type:lower() == "object" or type:lower() == "o" then
 | 
						||
            type = "Object"
 | 
						||
        else
 | 
						||
            return nil
 | 
						||
        end
 | 
						||
 | 
						||
        local distance = 0.0
 | 
						||
 | 
						||
        if Utility.Cache[type][id].coords ~= nil then
 | 
						||
            return _GetDistanceBetweenCoords(_GetEntityCoords(PlayerPedId()), Utility.Cache[type][id].coords, true)
 | 
						||
        else
 | 
						||
            return false
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    GetCoordOf = function(type, id)
 | 
						||
        if type:lower() == "marker" or type:lower() == "m" then
 | 
						||
            type = "Marker"
 | 
						||
        elseif type:lower() == "object" or type:lower() == "o" then
 | 
						||
            type = "Object"
 | 
						||
        else
 | 
						||
            return nil
 | 
						||
        end
 | 
						||
 | 
						||
        if DoesExist(type, id) then
 | 
						||
            return Utility.Cache[type][id].coords
 | 
						||
        else
 | 
						||
            developer("^1Error^0", "Unable to get the coords of the id", id)
 | 
						||
            return false
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    DoesExist = function(type, id)
 | 
						||
        if type:lower() == "marker" or type:lower() == "m" then
 | 
						||
            type = "Marker"
 | 
						||
        elseif type:lower() == "object" or type:lower() == "o" then
 | 
						||
            type = "Object"
 | 
						||
        elseif type:lower() == "n3d" or type:lower() == "n" then
 | 
						||
            type = "N3d"
 | 
						||
        else
 | 
						||
            return nil
 | 
						||
        end
 | 
						||
        
 | 
						||
        if Utility.Cache[type][id] ~= nil then
 | 
						||
            return true
 | 
						||
        else
 | 
						||
            return false
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
--// Camera //--
 | 
						||
    CreateCamera = function(coords, rotation, active, shake)
 | 
						||
        local cam = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
 | 
						||
 | 
						||
        SetCamCoord(cam, coords)
 | 
						||
 | 
						||
        if rotation ~= nil then
 | 
						||
            SetCamRot(cam, rotation.x, rotation.y, rotation.z)
 | 
						||
        end
 | 
						||
 | 
						||
        if shake ~= nil then
 | 
						||
            ShakeCam(cam, shake.type or "", shake.amount or 0.0)
 | 
						||
        end
 | 
						||
        
 | 
						||
        if active then
 | 
						||
            SetCamActive(cam, true)
 | 
						||
            RenderScriptCams(1, 1, 1500)
 | 
						||
        end
 | 
						||
 | 
						||
        return cam
 | 
						||
    end
 | 
						||
 | 
						||
    SwitchBetweenCam = function(old_cam, cam, duration)
 | 
						||
        SetCamActiveWithInterp(cam, old_cam, duration or 1500, 1, 1)
 | 
						||
        Citizen.Wait(duration or 1500)
 | 
						||
        DestroyCam(old_cam)
 | 
						||
    end 
 | 
						||
 | 
						||
--// Other // --
 | 
						||
    DevMode = function(state, time, format)
 | 
						||
        DevModeStatus = state
 | 
						||
 | 
						||
        if time == nil then time = true end
 | 
						||
        format = format or "%s %s %s"
 | 
						||
        
 | 
						||
        if state then
 | 
						||
            developer = function(action, type, id)
 | 
						||
                local _, _, _, hour, minute, second = GetLocalTime()
 | 
						||
 | 
						||
                if time then
 | 
						||
                    if type == nil then
 | 
						||
                        print(hour..":"..minute..":"..second.." - "..action)
 | 
						||
                    else
 | 
						||
                        print(hour..":"..minute..":"..second.." - "..string.format(format, action, type, id or ""))
 | 
						||
                    end
 | 
						||
                else
 | 
						||
                    if type == nil then
 | 
						||
                        print(action)
 | 
						||
                    else
 | 
						||
                        print(string.format(format, action, type, id or ""))
 | 
						||
                    end
 | 
						||
                end
 | 
						||
            end
 | 
						||
        else
 | 
						||
            developer = function() end
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    ReplaceTexture = function(prop, textureName, url, width, height)
 | 
						||
        local txName = prop..":"..textureName..":" -- prop:textureName 
 | 
						||
        local txId = txName..":"..url -- txName:url (prop:textureName:url)
 | 
						||
 | 
						||
        if not Utility.Cache.Textures[txId] then -- If texture with same prop, texture name and url does not exist we create it (to prevent 2 totally same dui)
 | 
						||
            local txd = CreateRuntimeTxd(txName..'duiTxd')
 | 
						||
            local duiObj = CreateDui(url, width, height)
 | 
						||
            local dui = GetDuiHandle(duiObj)
 | 
						||
            
 | 
						||
            CreateRuntimeTextureFromDuiHandle(txd, txName..'duiTex', dui)
 | 
						||
 | 
						||
            Utility.Cache.Textures[txId] = true
 | 
						||
        end
 | 
						||
 | 
						||
        AddReplaceTexture(prop, textureName, txName.."duiTxd", txName.."duiTex")
 | 
						||
    end
 | 
						||
 | 
						||
    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
 | 
						||
 | 
						||
--// Dialog //--
 | 
						||
    local function DialogueTable(entity, dialog, editing)
 | 
						||
        return {
 | 
						||
            Question = function(...) 
 | 
						||
                dialog.questions = {...}
 | 
						||
 | 
						||
                return DialogueTable(entity, dialog, editing)
 | 
						||
            end,
 | 
						||
 | 
						||
            Response = function(...)
 | 
						||
                local responses = {...}
 | 
						||
 | 
						||
                local formatted_text = {}
 | 
						||
                local no_formatted = {}
 | 
						||
 | 
						||
                for k1,v1 in pairs(responses) do
 | 
						||
                    no_formatted[k1] = {}
 | 
						||
 | 
						||
                    for k,v in pairs(v1) do
 | 
						||
                        if formatted_text[k1] == nil then
 | 
						||
                            formatted_text[k1] = ""
 | 
						||
                        end
 | 
						||
 | 
						||
                        formatted_text[k1] = formatted_text[k1]..k.."~w~ "..v.." | "
 | 
						||
 | 
						||
                        k = string.multigsub(k, {"%[", "%]"}, {"", ""})
 | 
						||
                        k = string.multigsub(k, {"~r~", "~b~", "~g~", "~y~", "~p~", "~o~", "~c~", "~m~", "~u~", "~n~", "~s~", "~w~"}, {"", "","", "","", "","", "","", "","", ""})
 | 
						||
 | 
						||
                        --print("k = "..k)
 | 
						||
                        no_formatted[k1][k] = v
 | 
						||
                    end
 | 
						||
 | 
						||
                    formatted_text[k1] = formatted_text[k1]:sub(1, -3)
 | 
						||
                end
 | 
						||
 | 
						||
                dialog.response = {
 | 
						||
                    no_formatted = no_formatted,
 | 
						||
                    formatted = formatted_text
 | 
						||
                }
 | 
						||
 | 
						||
                if editing then
 | 
						||
                    _TriggerEvent("Utility:Remove", "Dialogue", entity)
 | 
						||
                    _TriggerEvent("Utility:Create", "Dialogue", entity, dialog)
 | 
						||
                else
 | 
						||
                    _TriggerEvent("Utility:Create", "Dialogue", entity, dialog)
 | 
						||
                end
 | 
						||
                Utility.Cache.Dialogue[entity] = dialog
 | 
						||
 | 
						||
                return DialogueTable(entity, dialog, editing)
 | 
						||
            end,
 | 
						||
 | 
						||
            LastQuestion = function(last)
 | 
						||
                Utility.Cache.Dialogue[entity].lastQuestion = last
 | 
						||
                _TriggerEvent("Utility:Edit", "Dialogue", entity, "lastQuestion", last)
 | 
						||
 | 
						||
                return DialogueTable(entity, dialog, editing)
 | 
						||
            end
 | 
						||
        }
 | 
						||
    end
 | 
						||
 | 
						||
    StartDialogue = function(entity, distance, callback, stopWhenTalking)
 | 
						||
        local dialog = {
 | 
						||
            entity = entity,
 | 
						||
            distance = distance,
 | 
						||
            curQuestion = 1,
 | 
						||
            callback = callback,
 | 
						||
            stopWhenTalking = stopWhenTalking,
 | 
						||
            slice = GetEntitySlice(entity)
 | 
						||
        }
 | 
						||
 | 
						||
        developer("^2Created^0", "dialogue with entity", entity)
 | 
						||
        return DialogueTable(entity, dialog)
 | 
						||
    end
 | 
						||
 | 
						||
    EditDialogue = function(entity)
 | 
						||
        if entity ~= nil and IsEntityOnDialogue(entity) then
 | 
						||
            return DialogueTable(entity, Utility.Cache.Dialogue[entity], true)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    StopDialogue = function(entity, immediately)
 | 
						||
        if entity ~= nil and IsEntityOnDialogue(entity) then
 | 
						||
            developer("^1Stopping^0", "dialogue", entity)
 | 
						||
 | 
						||
            -- Set the current question as if it were the last one
 | 
						||
            
 | 
						||
            if immediately then
 | 
						||
                Utility.Cache.Dialogue[entity] = nil
 | 
						||
            else
 | 
						||
                local questions = Utility.Cache.Dialogue[entity].questions[1]
 | 
						||
                _TriggerEvent("Utility:Edit", "Dialogue", entity, "curQuestion", #questions)
 | 
						||
            end
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    IsEntityOnDialogue = function(entity)
 | 
						||
        return Utility.Cache.Dialogue[entity]
 | 
						||
    end
 | 
						||
 | 
						||
    RegisterNetEvent("Utility:DeleteDialogue", function(entity)
 | 
						||
        Utility.Cache.Dialogue[entity] = nil
 | 
						||
    end)
 | 
						||
 | 
						||
--// N3d //--
 | 
						||
    function GetScaleformsStatus()
 | 
						||
        local activeList = {}
 | 
						||
        local inactiveList = {}
 | 
						||
        for i = 1, 10 do
 | 
						||
            local scaleformName = "utility_lib_" .. i
 | 
						||
            if IsScaleformTaken(scaleformName) then
 | 
						||
                table.insert(activeList, {name = scaleformName, data = Utility.Cache.N3d[scaleformName]})
 | 
						||
            else
 | 
						||
                table.insert(inactiveList, {name = scaleformName, data = {txd = false, show = false, rotation = {}}})
 | 
						||
            end
 | 
						||
        end
 | 
						||
        return activeList, inactiveList
 | 
						||
    end
 | 
						||
 | 
						||
    function IsScaleformTaken(scaleformName)
 | 
						||
        return Utility.Cache.N3d[scaleformName] ~= nil
 | 
						||
    end
 | 
						||
 | 
						||
    local old_RequestScaleformMovie = RequestScaleformMovie
 | 
						||
    local function RequestScaleformMovie(scaleform)-- idk why but sometimes give error
 | 
						||
        --print(scaleform)
 | 
						||
 | 
						||
        local status, retval = pcall(old_RequestScaleformMovie, scaleform)
 | 
						||
 | 
						||
        while not status do
 | 
						||
            status, retval = pcall(old_RequestScaleformMovie, scaleform)
 | 
						||
            Citizen.Wait(1)
 | 
						||
        end
 | 
						||
 | 
						||
        return retval
 | 
						||
    end
 | 
						||
 | 
						||
    local function LoadScaleform(N3dHandle, scaleformName)
 | 
						||
        local scaleformHandle = RequestScaleformMovie(scaleformName) -- idk why but sometimes give error
 | 
						||
 | 
						||
        -- Wait till it has loaded
 | 
						||
        local startTimer = GetGameTimer()
 | 
						||
 | 
						||
        while not HasScaleformMovieLoaded(scaleformHandle) and (GetGameTimer() - startTimer) < 4000 do
 | 
						||
            Citizen.Wait(0)
 | 
						||
        end
 | 
						||
 | 
						||
        if (GetGameTimer() - startTimer) > 4000 then
 | 
						||
            developer("^1Error^0", "After 4000 ms to load the scaleform the scaleform has not loaded yet, try again or check that it has started correctly!")
 | 
						||
            return
 | 
						||
        end
 | 
						||
 | 
						||
        -- Save the handle in the table
 | 
						||
        Utility.Cache.N3d[N3dHandle].scaleform = scaleformHandle
 | 
						||
        _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "scaleform", scaleformHandle)
 | 
						||
    end
 | 
						||
 | 
						||
    local function StartupDui(N3dHandle, url, width, height)
 | 
						||
        local txd = CreateRuntimeTxd('txd'..N3dHandle) -- Create texture dictionary
 | 
						||
 | 
						||
        Utility.Cache.N3d[N3dHandle].dui = CreateDui(url, width, height) -- Create Dui with the url
 | 
						||
        _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "dui", Utility.Cache.N3d[N3dHandle].dui)
 | 
						||
 | 
						||
        while not IsDuiAvailable(Utility.Cache.N3d[N3dHandle].dui) do
 | 
						||
            Citizen.Wait(1)
 | 
						||
        end
 | 
						||
 | 
						||
        local dui = GetDuiHandle(Utility.Cache.N3d[N3dHandle].dui) -- Getting dui handle
 | 
						||
 | 
						||
        CreateRuntimeTextureFromDuiHandle(txd, 'txn'..N3dHandle, dui) -- Applying the txd on the dui
 | 
						||
 | 
						||
        if Utility.Cache.N3d[N3dHandle].scaleform ~= nil and not Utility.Cache.N3d[N3dHandle].txd then
 | 
						||
            BeginScaleformMovieMethod(Utility.Cache.N3d[N3dHandle].scaleform, 'SET_TEXTURE')
 | 
						||
 | 
						||
            ScaleformMovieMethodAddParamTextureNameString('txd'..N3dHandle) -- txd
 | 
						||
            ScaleformMovieMethodAddParamTextureNameString('txn'..N3dHandle) -- txn
 | 
						||
 | 
						||
            ScaleformMovieMethodAddParamInt(0) -- x
 | 
						||
            ScaleformMovieMethodAddParamInt(0) -- y
 | 
						||
            ScaleformMovieMethodAddParamInt(width)
 | 
						||
            ScaleformMovieMethodAddParamInt(height)
 | 
						||
 | 
						||
            EndScaleformMovieMethod()
 | 
						||
            Utility.Cache.N3d[N3dHandle].txd = true
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "txd", true)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    -- Class and handle
 | 
						||
    function CreateNui3d(scaleformName, url)
 | 
						||
        local N3dHandle = tostring(math.random(0, 9999))
 | 
						||
 | 
						||
        local _N3d = {
 | 
						||
            txd = false,
 | 
						||
            show = false,
 | 
						||
            rotation = {}
 | 
						||
        }
 | 
						||
 | 
						||
        Utility.Cache.N3d[N3dHandle] = _N3d
 | 
						||
        _TriggerEvent("Utility:Create", "N3d", N3dHandle, _N3d) -- Sync the table in the utility_lib
 | 
						||
 | 
						||
        -- Auto load the scaleform
 | 
						||
        LoadScaleform(N3dHandle, scaleformName)
 | 
						||
 | 
						||
        if url ~= nil then
 | 
						||
            developer("^2Starting^0", N3dHandle.." with url ".."nui://"..GetCurrentResourceName().."/"..url.." sf "..scaleformName)
 | 
						||
            StartupDui(N3dHandle, "nui://"..GetCurrentResourceName().."/"..url, 1920, 1080)
 | 
						||
        end
 | 
						||
 | 
						||
 | 
						||
        -- Class to return
 | 
						||
        local N3d_Class = {}
 | 
						||
        N3d_Class.__index = N3d_Class
 | 
						||
 | 
						||
        N3d_Class.init = function(self, url, width, height)
 | 
						||
            StartupDui(N3dHandle, "nui://"..GetCurrentResourceName().."/"..url, width or 1920, height or 1080)
 | 
						||
        end
 | 
						||
	
 | 
						||
        N3d_Class.datas = function(self)
 | 
						||
            return Utility.Cache.N3d[N3dHandle]
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.scale = function(self, scale)
 | 
						||
            Utility.Cache.N3d[N3dHandle].advanced_scale = scale
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "advanced_scale", scale)
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.rotation = function(self, rotation, syncedwithplayer)
 | 
						||
            Utility.Cache.N3d[N3dHandle].rotation.rotation = rotation
 | 
						||
            Utility.Cache.N3d[N3dHandle].rotation.syncedwithplayer = syncedwithplayer
 | 
						||
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "rotation", {
 | 
						||
                rotation = rotation,
 | 
						||
                syncedwithplayer = syncedwithplayer
 | 
						||
            })
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.destroy = function(self)
 | 
						||
            if Utility.Cache.N3d[N3dHandle].dui ~= nil then
 | 
						||
                DestroyDui(Utility.Cache.N3d[N3dHandle].dui)
 | 
						||
                SetScaleformMovieAsNoLongerNeeded(Utility.Cache.N3d[N3dHandle].scaleform)
 | 
						||
                Utility.Cache.N3d[N3dHandle] = nil
 | 
						||
                _TriggerEvent("Utility:Remove", "N3d", N3dHandle)
 | 
						||
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.started = function()
 | 
						||
            return Utility.Cache.N3d[N3dHandle].dui ~= nil
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.show = function(self, coords, scale)
 | 
						||
            Utility.Cache.N3d[N3dHandle].coords = coords
 | 
						||
            Utility.Cache.N3d[N3dHandle].scale = scale or 0.1
 | 
						||
            Utility.Cache.N3d[N3dHandle].show = true
 | 
						||
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "coords", coords)
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "scale", scale or 0.1)
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "show", true)
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.visible = function()
 | 
						||
            return Utility.Cache.N3d[N3dHandle].show
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.hide = function()
 | 
						||
            Utility.Cache.N3d[N3dHandle].show = false
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "show", false)
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.attach = function(self, entity, offset)
 | 
						||
            Utility.Cache.N3d[N3dHandle].attach = {
 | 
						||
                entity = entity,
 | 
						||
                offset = offset or vector3(0.0, 0.0, 0.0)
 | 
						||
            }
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "attach", {
 | 
						||
                entity = entity,
 | 
						||
                offset = offset or vector3(0.0, 0.0, 0.0)
 | 
						||
            })
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.detach = function(self, atcoords)
 | 
						||
            if atcoords then
 | 
						||
                Utility.Cache.N3d[N3dHandle].coords = GetEntityCoords(Utility.Cache.N3d[N3dHandle].attach.entity)
 | 
						||
                _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "coords", Utility.Cache.N3d[N3dHandle].coords)
 | 
						||
            end
 | 
						||
            Utility.Cache.N3d[N3dHandle].attach = nil
 | 
						||
            _TriggerEvent("Utility:Edit", "N3d", N3dHandle, "attach", nil)
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.object = function()
 | 
						||
            return Utility.Cache.N3d[N3dHandle].dui
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.msg = function(self, msg)
 | 
						||
            if self:started() then
 | 
						||
                SendDuiMessage(self:object(), json.encode(msg))
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        N3d_Class.replaceTexture = function(self, dict, textureName, wait)
 | 
						||
            if wait then
 | 
						||
                Citizen.Wait(wait)
 | 
						||
            end
 | 
						||
 | 
						||
            AddReplaceTexture(dict, textureName, 'txd'..N3dHandle, 'txn'..N3dHandle)
 | 
						||
        end
 | 
						||
 | 
						||
        return setmetatable({}, N3d_Class), N3dHandle
 | 
						||
    end
 | 
						||
 | 
						||
    AddEventHandler("onResourceStop", function(_resName)
 | 
						||
        if _resName == resName then
 | 
						||
            for i=1, #Utility.Cache.Events do
 | 
						||
                RemoveEventHandler(Utility.Cache.Events[i])
 | 
						||
            end
 | 
						||
 | 
						||
            for handle,data in pairs(Utility.Cache.N3d) do
 | 
						||
                if data.dui ~= nil and IsDuiAvailable(data.dui) then
 | 
						||
                    DestroyDui(data.dui)
 | 
						||
                end
 | 
						||
		
 | 
						||
                _TriggerEvent("Utility:Remove", "N3d", handle)
 | 
						||
            end
 | 
						||
	    if Utility.Cache.Settings.UseDelete then
 | 
						||
                for i=1, #Utility.Cache.EntityStack do
 | 
						||
                    local ent = Utility.Cache.EntityStack[i]
 | 
						||
                    NetworkRequestControlOfEntity(ent)
 | 
						||
                    if DoesEntityExist(ent) then
 | 
						||
                        DeleteEntity(ent)
 | 
						||
                    end
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end
 | 
						||
    end)
 | 
						||
 | 
						||
--// Animated Object Translations [Test] //--
 | 
						||
    -- Thanks to https://github.com/gre/bezier-easing for the incredible math behind this, i just converted the code to lua and did the NEWTON_MIN_SLOPE tweening, since precision rounding in lua seems to be different than in js.
 | 
						||
    -- by Gaëtan Renaudeau 2014 - 2015 – MIT License
 | 
						||
    
 | 
						||
    local NEWTON_ITERATIONS = 10
 | 
						||
    local NEWTON_MIN_SLOPE = 0.01
 | 
						||
    local SUBDIVISION_PRECISION = 0.0000001
 | 
						||
    local SUBDIVISION_MAX_ITERATIONS = 10
 | 
						||
 | 
						||
    local kSplineTableSize = 11
 | 
						||
    local kSampleStepSize = 1.0 / (kSplineTableSize - 1.0)
 | 
						||
 | 
						||
    local function A(aA1, aA2)
 | 
						||
        return 1.0 - 3.0 * aA2 + 3.0 * aA1
 | 
						||
    end
 | 
						||
 | 
						||
    local function B(aA1, aA2)
 | 
						||
        return 3.0 * aA2 - 6.0 * aA1
 | 
						||
    end
 | 
						||
 | 
						||
    local function C(aA1)
 | 
						||
        return 3.0 * aA1
 | 
						||
    end
 | 
						||
 | 
						||
    -- Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
 | 
						||
    local function calcBezier(aT, aA1, aA2)
 | 
						||
        return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT
 | 
						||
    end
 | 
						||
 | 
						||
    -- Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
 | 
						||
    local function getSlope(aT, aA1, aA2)
 | 
						||
        return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1)
 | 
						||
    end
 | 
						||
 | 
						||
    local function binarySubdivide(aX, aA, aB, mX1, mX2)
 | 
						||
        local currentX, currentT, i = 0, 0, 0
 | 
						||
        repeat
 | 
						||
            currentT = aA + (aB - aA) / 2.0
 | 
						||
            currentX = calcBezier(currentT, mX1, mX2) - aX
 | 
						||
            if currentX > 0.0 then
 | 
						||
                aB = currentT
 | 
						||
            else
 | 
						||
                aA = currentT
 | 
						||
            end
 | 
						||
 | 
						||
            i = i + 1
 | 
						||
        until math.abs(currentX) <= SUBDIVISION_PRECISION or i >= SUBDIVISION_MAX_ITERATIONS
 | 
						||
 | 
						||
        return currentT
 | 
						||
    end
 | 
						||
 | 
						||
    local function newtonRaphsonIterate(aX, aGuessT, mX1, mX2)
 | 
						||
        for i = 1, NEWTON_ITERATIONS do
 | 
						||
            local currentSlope = getSlope(aGuessT, mX1, mX2)
 | 
						||
            if currentSlope == 0.0 then
 | 
						||
                return aGuessT
 | 
						||
            end
 | 
						||
 | 
						||
            local currentX = calcBezier(aGuessT, mX1, mX2) - aX
 | 
						||
            aGuessT = aGuessT - currentX / currentSlope
 | 
						||
        end
 | 
						||
        return aGuessT
 | 
						||
    end
 | 
						||
 | 
						||
    local function LinearEasing(x)
 | 
						||
        return x
 | 
						||
    end
 | 
						||
 | 
						||
    local function bezier(mX1, mY1, mX2, mY2)
 | 
						||
        if not (0 <= mX1 and mX1 <= 1 and 0 <= mX2 and mX2 <= 1) then
 | 
						||
            error('bezier x values must be in [0, 1] range')
 | 
						||
        end
 | 
						||
 | 
						||
        if mX1 == mY1 and mX2 == mY2 then
 | 
						||
            return LinearEasing
 | 
						||
        end
 | 
						||
 | 
						||
        -- Precompute samples table
 | 
						||
        local sampleValues = {}
 | 
						||
        for i = 1, kSplineTableSize do
 | 
						||
            sampleValues[i] = calcBezier((i - 1) * kSampleStepSize, mX1, mX2)
 | 
						||
        end
 | 
						||
 | 
						||
        local function getTForX(aX)
 | 
						||
            local intervalStart = 0.0
 | 
						||
            local currentSample = 1
 | 
						||
            local lastSample = kSplineTableSize - 1
 | 
						||
 | 
						||
            while currentSample ~= lastSample and sampleValues[currentSample] <= aX do
 | 
						||
                intervalStart = intervalStart + kSampleStepSize
 | 
						||
                currentSample = currentSample + 1
 | 
						||
            end
 | 
						||
 | 
						||
            currentSample = currentSample - 1
 | 
						||
 | 
						||
            -- Interpolate to provide an initial guess for t
 | 
						||
            local dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample])
 | 
						||
            local guessForT = intervalStart + dist * kSampleStepSize
 | 
						||
            local initialSlope = getSlope(guessForT, mX1, mX2)
 | 
						||
 | 
						||
            if initialSlope >= NEWTON_MIN_SLOPE then
 | 
						||
                return newtonRaphsonIterate(aX, guessForT, mX1, mX2)
 | 
						||
            elseif initialSlope == 0.0 then
 | 
						||
                return guessForT
 | 
						||
            else
 | 
						||
                return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2)
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        return function(x)
 | 
						||
            if x == 0 or x == 1 then
 | 
						||
                return x
 | 
						||
            end
 | 
						||
 | 
						||
            return calcBezier(getTForX(x), mY1, mY2)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    local predefinedCubicBeziers = {
 | 
						||
        ease = {0.25, 0.1, 0.25, 1},
 | 
						||
        easeIn = {0.42, 0, 1, 1},
 | 
						||
        easeOut = {0, 0, 0.58, 1},
 | 
						||
        easeInOut = {0.42, 0, 0.58, 1},
 | 
						||
    }
 | 
						||
 | 
						||
    TranslateObjectCoordsCubicBezier = function(obj, destination, duration, cubicBezier)
 | 
						||
        local startCoords = GetEntityCoords(obj)
 | 
						||
        local startTimer = GetNetworkTimeAccurate()
 | 
						||
 | 
						||
        local timer = GetNetworkTimeAccurate()
 | 
						||
        local done = false
 | 
						||
        local space = destination - startCoords
 | 
						||
        local axis = {"x", "y", "z"}
 | 
						||
 | 
						||
        if type(cubicBezier) == "string" then
 | 
						||
            cubicBezier = predefinedCubicBeziers[cubicBezier]
 | 
						||
 | 
						||
            if not cubicBezier then
 | 
						||
                error("TranslateObjectCoordsCubicBezier: `"..cubicBezier.."` its not a predefined cubic bezier")
 | 
						||
                return
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        local easingFunction = bezier(table.unpack(cubicBezier))
 | 
						||
 | 
						||
        while not done and (timer - startTimer) < duration do
 | 
						||
            Citizen.Wait(0) -- Wait 1 tick
 | 
						||
            local timeAccurate = GetNetworkTimeAccurate()
 | 
						||
 | 
						||
            if timer ~= 0 and (timeAccurate - timer) ~= 0 then -- If some time has elapsed since the last call
 | 
						||
                local speed = {}
 | 
						||
                local progress = (timeAccurate - startTimer) / (duration)
 | 
						||
 | 
						||
                for k, v in pairs(axis) do
 | 
						||
                    if startCoords[v] == destination[v] then
 | 
						||
                        speed[v] = 0
 | 
						||
                    else
 | 
						||
                        local updatedCoords = GetEntityCoords(obj)
 | 
						||
                        local newCoord = math.lerp(startCoords[v], destination[v], easingFunction(progress)) -- we get the new coords from lerp and the easing function for the translation progress
 | 
						||
                        local distance = math.abs(updatedCoords[v] - newCoord)
 | 
						||
    
 | 
						||
                        speed[v] = distance
 | 
						||
                    end
 | 
						||
                end
 | 
						||
                
 | 
						||
                done = SlideObject(obj, destination, speed.x, speed.y, speed.z, false)
 | 
						||
            end
 | 
						||
 | 
						||
            timer = timeAccurate
 | 
						||
        end
 | 
						||
 | 
						||
        SetEntityCoords(obj, destination)
 | 
						||
    end
 | 
						||
 | 
						||
    TranslateObjectCoords = function(obj, destination, duration)
 | 
						||
        TranslateObjectCoordsCubicBezier(obj, destination, duration, {0.1, 0.1, 0.1, 0.1})
 | 
						||
    end
 | 
						||
    TranslateUniformRectilinearMotion = TranslateObjectCoords
 | 
						||
 | 
						||
    TranslateObjectRotationCubicBezier = function(obj, destination, duration, rotationOrder, cubicBezier)
 | 
						||
        local startRotation = GetEntityRotation(obj)
 | 
						||
        local startTimer = GetNetworkTimeAccurate()
 | 
						||
 | 
						||
        local timer = GetNetworkTimeAccurate()
 | 
						||
        local axis = {"x", "y", "z"}
 | 
						||
        local deltaRotation = {}
 | 
						||
 | 
						||
        -- Normalize destination to [-180, 180] range and compute shortest angular path
 | 
						||
        for _, v in ipairs(axis) do
 | 
						||
            local start = startRotation[v]
 | 
						||
            local dest = destination[v]
 | 
						||
            local delta = ((dest - start + 180) % 360) - 180
 | 
						||
 | 
						||
            deltaRotation[v] = delta
 | 
						||
        end
 | 
						||
 | 
						||
        if type(cubicBezier) == "string" then
 | 
						||
            cubicBezier = predefinedCubicBeziers[cubicBezier]
 | 
						||
 | 
						||
            if not cubicBezier then
 | 
						||
                error("TranslateObjectCoordsCubicBezier: `"..cubicBezier.."` its not a predefined cubic bezier")
 | 
						||
                return
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        local easingFunction = bezier(table.unpack(cubicBezier))
 | 
						||
 | 
						||
        while (timer - startTimer) < duration do
 | 
						||
            Citizen.Wait(0) -- Wait 1 tick
 | 
						||
            local timeAccurate = GetNetworkTimeAccurate()
 | 
						||
            local progress = (timeAccurate - startTimer) / (duration)
 | 
						||
 | 
						||
            if timer ~= 0 and (timeAccurate - timer) ~= 0 then -- If some time has elapsed since the last call
 | 
						||
                local rot = {}
 | 
						||
 | 
						||
                for k, v in ipairs(axis) do
 | 
						||
                    local start = startRotation[v]
 | 
						||
                    local delta = deltaRotation[v]
 | 
						||
 | 
						||
                    rot[v] = start + delta * easingFunction(progress)
 | 
						||
                end
 | 
						||
                
 | 
						||
                SetEntityRotation(obj, rot.x, rot.y, rot.z, rotationOrder or 2)
 | 
						||
            end
 | 
						||
 | 
						||
            timer = timeAccurate
 | 
						||
        end
 | 
						||
 | 
						||
        SetEntityRotation(obj, destination.x, destination.y, destination.z, rotationOrder or 2)
 | 
						||
    end
 | 
						||
 | 
						||
    TranslateObjectRotation = function(obj, destination, duration, rotationOrder)
 | 
						||
        TranslateObjectRotationCubicBezier(obj, destination, duration, rotationOrder, {0.1, 0.1, 0.1, 0.1})
 | 
						||
    end
 | 
						||
 | 
						||
--// Heists //--
 | 
						||
    -- Scene
 | 
						||
        CreateScene = function(coords, rot, holdLastFrame, looped, animSpeed, animTime)
 | 
						||
            local scene = NetworkCreateSynchronisedScene(coords, rot, 2, holdLastFrame or false, looped or false, 1065353216, animTime or 0, animSpeed or 1.3)
 | 
						||
            Utility.Cache.Scenes[scene] = {
 | 
						||
                coords = coords,
 | 
						||
                rotation = rot,
 | 
						||
                players = {},
 | 
						||
                entities = {},
 | 
						||
                dicts = {},
 | 
						||
            }
 | 
						||
            
 | 
						||
            return scene
 | 
						||
        end
 | 
						||
 | 
						||
        AddEntityToScene = function(entity, scene, dict, name, speed, speedMultiplier, flag)
 | 
						||
            if not DoesEntityExist(tonumber(entity)) then
 | 
						||
                local model = entity
 | 
						||
                local coords = GetEntityCoords(PlayerPedId())
 | 
						||
                entity = CreateObject(entity, coords + vector3(0,0, -4.0), true)
 | 
						||
                SetEntityCollision(entity, false, true)
 | 
						||
 | 
						||
                Utility.Cache.Scenes[scene].entities[model] = entity -- if the entity was created by the scene then it will have automatic handling, otherwise you will have to delete it yourself manually
 | 
						||
            end
 | 
						||
 | 
						||
            RequestAnimDict(dict)
 | 
						||
            while not HasAnimDictLoaded(dict) do
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            developer("^3Scenes^0", "Adding object", entity, "to scene", scene, "[", dict, name, "]")
 | 
						||
            NetworkAddEntityToSynchronisedScene(entity, scene, dict, name, speed or 4.0, speedMultiplier or -8.0, flag or 1)
 | 
						||
            table.insert(Utility.Cache.Scenes[scene].dicts, dict)
 | 
						||
        end
 | 
						||
 | 
						||
        AddPlayerToScene = function(player, scene, dict, name, ...)
 | 
						||
            Citizen.InvokeNative(0x144da052257ae7d8, true) -- synchronize the scene with any player that is in the scene
 | 
						||
 | 
						||
            local ped = DoesEntityExist(player) and player or GetPlayerPed(player) -- (player id) or (player ped id) are accepted
 | 
						||
            AddPedToScene(ped, scene, dict, name, ...)
 | 
						||
 | 
						||
            Utility.Cache.Scenes[scene].players[ped] = {
 | 
						||
                dict = dict,
 | 
						||
                name = name
 | 
						||
            }
 | 
						||
        end
 | 
						||
 | 
						||
        AddPedToScene = function(ped, scene, dict, name, blendIn, blendOut, duration, flag)
 | 
						||
            if not DoesEntityExist(ped) then
 | 
						||
                local model = ped
 | 
						||
                ped = CreatePed(ped, vector3(0,0,0), 0.0, true)
 | 
						||
                
 | 
						||
                Utility.Cache.Scenes[scene].entities[model] = ped -- if the entity was created by the scene then it will have automatic handling, otherwise you will have to delete it yourself manually
 | 
						||
            end
 | 
						||
 | 
						||
            RequestAnimDict(dict)
 | 
						||
            while not HasAnimDictLoaded(dict) do
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            developer("^3Scenes^0", "Adding ped", ped, "to scene", scene, "[", dict, name, "]")
 | 
						||
            NetworkAddPedToSynchronisedScene(ped, scene, dict, name, blendIn or 1.5, blendOut or -4.0, duration or 1, flag or 16, 0, 0)
 | 
						||
            table.insert(Utility.Cache.Scenes[scene].dicts, dict)
 | 
						||
        end
 | 
						||
 | 
						||
        GoNearInitialOffset = function(player, coords, rot, dict, name)
 | 
						||
            -- Taken from https://github.com/root-cause/v-decompiled-scripts/blob/master/fm_mission_controller.c line 752898
 | 
						||
            local ped = DoesEntityExist(player) and player or GetPlayerPed(player) -- (player id) or (player ped id) are accepted
 | 
						||
            local heading = rot and rot.z or GetEntityHeading(ped)
 | 
						||
            
 | 
						||
            --Citizen.Wait(5000)
 | 
						||
 | 
						||
            RequestAnimDict(dict)
 | 
						||
            while not HasAnimDictLoaded(dict) do
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            local pos = GetAnimInitialOffsetPosition(dict, name, coords, 0.0, 0.0, heading, 0.0, 2)
 | 
						||
            local rot = GetAnimInitialOffsetRotation(dict, name, coords, 0.0, 0.0, heading, 0.0, 2)
 | 
						||
            
 | 
						||
            RemoveAnimDict(dict)
 | 
						||
 | 
						||
            TaskGoStraightToCoord(ped, pos, 0.6, -1, rot.z, 0.4)
 | 
						||
            --TaskFollowNavMeshToCoord(ped, pos, 0.6, -1, 0.1, true)
 | 
						||
 | 
						||
            -- Wait until it is close to the start zone
 | 
						||
            local startCheckingDistance = GetGameTimer()
 | 
						||
 | 
						||
            --DebugCoords(coords)
 | 
						||
            --DebugCoords(pos)
 | 
						||
 | 
						||
            while (#(GetEntityCoords(ped) - pos) > 0.3) and (GetGameTimer() - startCheckingDistance) < 4000 do
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            --TaskAchieveHeading(ped, rot.z, 1000)
 | 
						||
            --Citizen.Wait(1000)
 | 
						||
 | 
						||
            -- Wait until he has stopped
 | 
						||
            while GetEntitySpeed(ped) > 0.2 and (GetGameTimer() - startCheckingDistance) < 4000 do
 | 
						||
                Citizen.Wait(50)
 | 
						||
            end
 | 
						||
 | 
						||
            -- Let's add a break just in case (weird bugs can happen without it)
 | 
						||
            --Citizen.Wait(1000)
 | 
						||
 | 
						||
            if (GetGameTimer() - startCheckingDistance) >= 4000 then
 | 
						||
                TaskPedSlideToCoord(ped, pos, heading, 2000)
 | 
						||
                Citizen.Wait(2000)
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        StartScene = function(scene, goNearInitialOffset)
 | 
						||
            local curScene = Utility.Cache.Scenes[scene]
 | 
						||
 | 
						||
            if goNearInitialOffset then
 | 
						||
                for ped, v in pairs(curScene.players) do
 | 
						||
                    GoNearInitialOffset(ped, curScene.coords, curScene.rotation, v.dict, v.name)
 | 
						||
                end
 | 
						||
            end
 | 
						||
 | 
						||
            NetworkStartSynchronisedScene(scene)
 | 
						||
        end
 | 
						||
 | 
						||
        StopScene = function(scene)
 | 
						||
            developer("^3Scenes^0", "Stop scene", scene)
 | 
						||
            NetworkStopSynchronisedScene(scene)
 | 
						||
 | 
						||
            local curScene = Utility.Cache.Scenes[scene]
 | 
						||
 | 
						||
            -- Delete create entities
 | 
						||
            for model, entity in pairs(curScene.entities) do
 | 
						||
                developer("^3Scenes^0", "Deleting entity", entity)
 | 
						||
                DeleteEntity(entity)
 | 
						||
            end
 | 
						||
 | 
						||
            -- Unload anim dicts
 | 
						||
            for i=1, #curScene.dicts do
 | 
						||
                RemoveAnimDict(curScene.dicts[i])
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        GetSceneEntity = function(scene, model)
 | 
						||
            if model then
 | 
						||
                return Utility.Cache.Scenes[scene].entities[model]
 | 
						||
            else
 | 
						||
                return Utility.Cache.Scenes[scene].entities
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
    -- Thermal Charge
 | 
						||
        local StartPlantThermalChargeScene = function(door, coords)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local rot = GetEntityRotation(door)
 | 
						||
            
 | 
						||
            --DebugCoords(coords)
 | 
						||
            --GoNearInitialOffset(ped, coords, "anim@heists@ornate_bank@thermal_charge", "thermal_charge")
 | 
						||
 | 
						||
            local scene = CreateScene(coords, rot)
 | 
						||
            AddPlayerToScene(ped, scene, "anim@heists@ornate_bank@thermal_charge", "thermal_charge")
 | 
						||
            AddEntityToScene("hei_p_m_bag_var22_arm_s", scene, "anim@heists@ornate_bank@thermal_charge", "bag_thermal_charge")
 | 
						||
            StartScene(scene, true)
 | 
						||
 | 
						||
            return scene
 | 
						||
        end
 | 
						||
 | 
						||
        local FindDoorLockCoords = function(door)
 | 
						||
            local size = GetEntitySize(door)
 | 
						||
 | 
						||
            if doorHash == GetHashKey("hei_v_ilev_bk_safegate_pris") then
 | 
						||
                --                                                                 SafePedCoords
 | 
						||
                return GetOffsetFromEntityInWorldCoords(door, -(size.x - 0.1), -0.05, 0.0)
 | 
						||
            else
 | 
						||
                --                                                                SafePedCoords
 | 
						||
                return GetOffsetFromEntityInWorldCoords(door, (size.x - 0.1), -0.05, 0.0)
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        local PullOutThermalCharge = function(ped, coords)
 | 
						||
            local thermal = CreateObject("hei_prop_heist_thermite", coords - vector3(0, 0, 5), true)
 | 
						||
 | 
						||
            SetEntityCollision(thermal, false, false)
 | 
						||
            AttachEntityToEntity(thermal, ped, GetPedBoneIndex(ped, 28422), 0, 0, 0, 0, 0, 200.0, true, true, false, true, 1, true)
 | 
						||
 | 
						||
            return thermal
 | 
						||
        end
 | 
						||
 | 
						||
        local PlantThermalCharge = function(thermal)
 | 
						||
            DetachEntity(thermal, true, true)
 | 
						||
        end
 | 
						||
 | 
						||
        local StartThermalChargeEffect = function(thermal)
 | 
						||
            return StartParticleFxOnNetworkEntity("scr_ornate_heist", "scr_heist_ornate_thermal_burn", thermal, vector3(0.0, 1.0, -0.1), vector3(0.0, 0.0, 0.0), 1.0)
 | 
						||
        end
 | 
						||
 | 
						||
        local CoverEyesFromThermalCharge = function(ped)
 | 
						||
            TaskPlayAnim(ped, "anim@heists@ornate_bank@thermal_charge", "cover_eyes_loop", 1.5, 1.0, -1, 51, 1, 0, 0, 0)
 | 
						||
        end
 | 
						||
 | 
						||
        local GetMoltenModel = function(door)
 | 
						||
            local model = GetEntityModel(door)
 | 
						||
 | 
						||
            if model == GetHashKey("hei_v_ilev_bk_gate_pris") then
 | 
						||
                return "hei_v_ilev_bk_gate_molten"
 | 
						||
 | 
						||
            elseif model == GetHashKey("hei_v_ilev_bk_gate2_pris") then
 | 
						||
                return "hei_v_ilev_bk_gate2_molten"
 | 
						||
                
 | 
						||
            elseif model == GetHashKey("hei_v_ilev_bk_safegate_pris") then
 | 
						||
                return "hei_v_ilev_bk_safegate_molten"
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        local ChangeDoorModel = function(door)
 | 
						||
            local moltenModel = GetMoltenModel(door)
 | 
						||
 | 
						||
            if moltenModel then
 | 
						||
                SetEntityModel(door, moltenModel)
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        local StopThermalChargeEffect = function(ped, thermal)  
 | 
						||
            DeleteObject(thermal)
 | 
						||
            TaskPlayAnim(ped, "anim@heists@ornate_bank@thermal_charge", "cover_eyes_exit", 1.0, 8.0, 1000, 51, 1, 0, 0, 0)
 | 
						||
 | 
						||
            Citizen.Wait(1000)
 | 
						||
            ClearPedTasks(ped)
 | 
						||
        end
 | 
						||
 | 
						||
        BreakDoorWithThermalCharge = function(door, bagComponent, duration)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local doorLock = FindDoorLockCoords(door)
 | 
						||
 | 
						||
            local scene = StartPlantThermalChargeScene(door, doorLock)
 | 
						||
 | 
						||
            SetPedComponentVariation(ped, 5, 0, 0, 0) -- Remove real bag from player
 | 
						||
            Citizen.Wait(1000)
 | 
						||
            local thermal = PullOutThermalCharge(ped, doorLock)
 | 
						||
 | 
						||
            Citizen.Wait(3000)
 | 
						||
            PlantThermalCharge(thermal)
 | 
						||
 | 
						||
            --print("start effect")
 | 
						||
            Citizen.Wait(1000)
 | 
						||
            local effect = StartThermalChargeEffect(thermal)
 | 
						||
            
 | 
						||
            --print("stop scene")
 | 
						||
            StopScene(scene)
 | 
						||
            SetPedComponentVariation(ped, 5, bagComponent or 45, 0, 0) -- Reset real bag to player
 | 
						||
 | 
						||
            developer("^3Scenes^0", "Cover eyes")
 | 
						||
            --print("cover eyes")
 | 
						||
            CoverEyesFromThermalCharge(ped)
 | 
						||
            Citizen.Wait(1000)
 | 
						||
            ChangeDoorModel(door)
 | 
						||
            developer("^3Scenes^0", "Wait "..(duration or 3000))
 | 
						||
            Citizen.Wait(duration or 3000)
 | 
						||
            StopThermalChargeEffect(ped, thermal)
 | 
						||
        end
 | 
						||
 | 
						||
    -- Trolly
 | 
						||
        -- Create
 | 
						||
        local GetTrollyModel = function(type)
 | 
						||
            if type == "cash" then
 | 
						||
                return "hei_prop_hei_cash_trolly_01"
 | 
						||
            elseif type == "gold" then
 | 
						||
                return "ch_prop_gold_trolly_01a"
 | 
						||
            elseif type == "diamond" then
 | 
						||
                return "ch_prop_diamond_trolly_01a"
 | 
						||
            end
 | 
						||
        end
 | 
						||
        
 | 
						||
        local GenerateTrollyId = function(type)
 | 
						||
            return "utility_heist:"..type.."_trolly:"..math.random(1, 10000) -- example: utility_heist:cash_trolly:3910
 | 
						||
        end
 | 
						||
        
 | 
						||
        CreateTrolly = function(type, coords, giveCash, notify, repeatedlyPress, minSpeed, maxSpeed, networked)
 | 
						||
            if type(repeatedlyPress) == "number" then -- For backwards compatibility
 | 
						||
                networked = maxSpeed
 | 
						||
                maxSpeed = minSpeed
 | 
						||
                minSpeed = repeatedlyPress
 | 
						||
            end
 | 
						||
 | 
						||
            local obj = nil
 | 
						||
            local id = GenerateTrollyId(type) -- Pseudo random id
 | 
						||
        
 | 
						||
            -- Object creation
 | 
						||
            if type == "cash" then
 | 
						||
                obj = CreateObject("hei_prop_hei_cash_trolly_01", coords, networked)
 | 
						||
            elseif type == "gold" then
 | 
						||
                obj = CreateObject("ch_prop_gold_trolly_01a", coords, networked)
 | 
						||
            elseif type == "diamond" then
 | 
						||
                obj = CreateObject("ch_prop_diamond_trolly_01a", coords, networked)
 | 
						||
            end
 | 
						||
            
 | 
						||
            PlaceObjectOnGroundProperly(obj)
 | 
						||
        
 | 
						||
            -- Marker and data creation
 | 
						||
            CreateMarker(id, coords, 0.0, 2.0, {notify = notify or "Press {E} to begin looting the trolly"})
 | 
						||
            SetFor(id, "minSpeed", minSpeed)
 | 
						||
            SetFor(id, "maxSpeed", maxSpeed)
 | 
						||
            SetFor(id, "giveCash", giveCash)
 | 
						||
            SetFor(id, "repeatedlyPress", repeatedlyPress)
 | 
						||
 | 
						||
            local eventHandler = nil
 | 
						||
            eventHandler = On("marker", function(_id)
 | 
						||
                if _id == id then
 | 
						||
                    local type = id:match("utility_heist:(%w+)_trolly")
 | 
						||
        
 | 
						||
                    local coords = GetEntityCoords(PlayerPedId())
 | 
						||
                    local model = GetTrollyModel(type)
 | 
						||
                    local trollyObj = GetClosestObjectOfType(coords, 3.0, GetHashKey(model))
 | 
						||
                        
 | 
						||
                    DeleteMarker(id)
 | 
						||
                    Citizen.Wait(500)
 | 
						||
                    ClearAllHelpMessages()
 | 
						||
        
 | 
						||
                    if trollyObj > 0 then
 | 
						||
                        LootTrolly(id, type, trollyObj)
 | 
						||
                        RemoveEventHandler(eventHandler)
 | 
						||
                    end
 | 
						||
                end
 | 
						||
            end)
 | 
						||
 | 
						||
            return id, obj
 | 
						||
        end
 | 
						||
 | 
						||
        -- Loot
 | 
						||
        local GetEmptyTrollyModel = function(type)
 | 
						||
            if type == "cash" then
 | 
						||
                return "hei_prop_hei_cash_trolly_03"
 | 
						||
            else
 | 
						||
                return "hei_prop_hei_cash_trolly_03"
 | 
						||
                --return "ch_prop_gold_trolly_empty"
 | 
						||
            end
 | 
						||
        end
 | 
						||
        
 | 
						||
        local GetTrollyCashProp = function(type)
 | 
						||
            if type == "cash" then
 | 
						||
                return "hei_prop_heist_cash_pile"
 | 
						||
            elseif type == "gold" then
 | 
						||
                return "ch_prop_gold_bar_01a"
 | 
						||
            elseif type == "diamond" then
 | 
						||
                return "ch_prop_vault_dimaondbox_01a"
 | 
						||
            end
 | 
						||
        end
 | 
						||
        
 | 
						||
        local CollectCashProp = function(id, giveCash)
 | 
						||
            PlaySoundFrontend(-1, "LOCAL_PLYR_CASH_COUNTER_INCREASE", "DLC_HEISTS_GENERAL_FRONTEND_SOUNDS", true)
 | 
						||
            giveCash() -- Give cash function
 | 
						||
        end
 | 
						||
        
 | 
						||
        local CreateCashProp = function(id, model, giveCash)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local coords = GetEntityCoords(ped)
 | 
						||
            local cashProp = CreateObject(model, coords, true)
 | 
						||
        
 | 
						||
            FreezeEntityPosition(cashProp, true)
 | 
						||
            SetEntityInvincible(cashProp, true)
 | 
						||
            SetEntityNoCollisionEntity(cashProp, ped)
 | 
						||
            SetEntityVisible(cashProp, false, false)
 | 
						||
            AttachEntityToEntity(cashProp, ped, GetPedBoneIndex(ped, 60309), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 0, true)
 | 
						||
            DisableCamCollisionForEntity(cashProp)
 | 
						||
        
 | 
						||
            Utility.Cache.LootingTrolly = true
 | 
						||
        
 | 
						||
            Citizen.CreateThread(function()
 | 
						||
                local eventCashAppear = GetHashKey("CASH_APPEAR")
 | 
						||
                local eventReleaseCashDestroy = GetHashKey("RELEASE_CASH_DESTROY")
 | 
						||
        
 | 
						||
                while Utility.Cache.LootingTrolly do            
 | 
						||
                    if HasAnimEventFired(ped, eventCashAppear) then
 | 
						||
                        SetEntityVisible(cashProp, true, false) -- Set entity visible
 | 
						||
                    end
 | 
						||
                    if HasAnimEventFired(ped, eventReleaseCashDestroy) then
 | 
						||
                        if IsEntityVisible(cashProp) then -- Set Entity invisible
 | 
						||
                            SetEntityVisible(cashProp, false, false)
 | 
						||
        
 | 
						||
                            CollectCashProp(id, giveCash)
 | 
						||
                        end
 | 
						||
                    end
 | 
						||
        
 | 
						||
                    Citizen.Wait(1)
 | 
						||
                end
 | 
						||
                DeleteObject(cashProp)
 | 
						||
            end)
 | 
						||
        end
 | 
						||
        
 | 
						||
        local StartLootIntroScene = function(bag, trolly)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local coords = GetEntityCoords(trolly)
 | 
						||
            local rot = GetEntityRotation(trolly)
 | 
						||
        
 | 
						||
            local scene = CreateScene(coords, rot)
 | 
						||
            AddPlayerToScene(ped, scene, "anim@heists@ornate_bank@grab_cash", "intro")
 | 
						||
            AddEntityToScene(bag, scene, "anim@heists@ornate_bank@grab_cash", "bag_intro")
 | 
						||
            StartScene(scene, true)
 | 
						||
        
 | 
						||
            return scene
 | 
						||
        end
 | 
						||
        
 | 
						||
        local StartPlayerInteractionGrabLoop = function(grabScene, min, max, repeatedlyPress)
 | 
						||
            local lscene = NetworkGetLocalSceneFromNetworkId(grabScene)
 | 
						||
            local speed = min
 | 
						||
            local finished = false
 | 
						||
        
 | 
						||
            -- Every mouse click add 0.1 to the speed
 | 
						||
            Citizen.CreateThread(function()
 | 
						||
                while not finished do
 | 
						||
                    if IsControlJustPressed(0, 24) then
 | 
						||
                        if speed <= max then
 | 
						||
                            speed = speed + 0.1
 | 
						||
                        end
 | 
						||
                    end
 | 
						||
                    Citizen.Wait(0)
 | 
						||
                end
 | 
						||
            end)
 | 
						||
        
 | 
						||
            -- Wait that the scene start
 | 
						||
            while not IsSynchronizedSceneRunning(lscene) do
 | 
						||
                lscene = NetworkGetLocalSceneFromNetworkId(grabScene)
 | 
						||
                Citizen.Wait(1)
 | 
						||
            end
 | 
						||
 | 
						||
            Citizen.CreateThread(function()
 | 
						||
                AddTextEntry('PersistentButtonNotification', repeatedlyPress or "Repeatedly press ~INPUT_SCRIPT_RDOWN~ to grab faster")
 | 
						||
                BeginTextCommandDisplayHelp('PersistentButtonNotification')
 | 
						||
                EndTextCommandDisplayHelp(0, true, true, -1)
 | 
						||
            end)
 | 
						||
        
 | 
						||
            -- If the scene is still running, remove 0.1 every 300ms
 | 
						||
            while GetSynchronizedScenePhase(lscene) < 0.99 do
 | 
						||
                lscene = NetworkGetLocalSceneFromNetworkId(grabScene)
 | 
						||
        
 | 
						||
                if speed > min then
 | 
						||
                    speed = speed - 0.1
 | 
						||
                end
 | 
						||
                
 | 
						||
                SetSynchronizedSceneRate(lscene, speed)
 | 
						||
                Citizen.Wait(300)
 | 
						||
            end
 | 
						||
        
 | 
						||
            ClearAllHelpMessages()
 | 
						||
            finished = true
 | 
						||
            --print("Finished grabbing money")
 | 
						||
        end
 | 
						||
        
 | 
						||
        local StartLootGrabScene = function(bag, trolly)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local coords = GetEntityCoords(trolly)
 | 
						||
            local rot = GetEntityRotation(trolly)
 | 
						||
        
 | 
						||
            local scene = CreateScene(coords, rot)
 | 
						||
            AddPlayerToScene(ped, scene, "anim@heists@ornate_bank@grab_cash", "grab")
 | 
						||
            AddEntityToScene(bag, scene, "anim@heists@ornate_bank@grab_cash", "bag_grab")
 | 
						||
            AddEntityToScene(trolly, scene, "anim@heists@ornate_bank@grab_cash", "cart_cash_dissapear")
 | 
						||
            StartScene(scene)
 | 
						||
        
 | 
						||
            return scene
 | 
						||
        end
 | 
						||
        local StartLootExitScene = function(bag, trolly)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local coords = GetEntityCoords(trolly)
 | 
						||
            local rot = GetEntityRotation(trolly)
 | 
						||
        
 | 
						||
            local scene = CreateScene(coords, rot)
 | 
						||
            AddPlayerToScene(ped, scene, "anim@heists@ornate_bank@grab_cash", "exit")
 | 
						||
            AddEntityToScene(bag, scene, "anim@heists@ornate_bank@grab_cash", "bag_exit")
 | 
						||
            StartScene(scene)
 | 
						||
        
 | 
						||
            return scene
 | 
						||
        end
 | 
						||
        
 | 
						||
        LootTrolly = function(id, type, trolly)
 | 
						||
            local ped = PlayerPedId()
 | 
						||
            local cashPropModel = GetTrollyCashProp(type)
 | 
						||
            local emptyTrolly = GetEmptyTrollyModel(type)
 | 
						||
            local options = GetFrom(id)
 | 
						||
 | 
						||
        
 | 
						||
            if IsEntityPlayingAnim(trolly, "anim@heists@ornate_bank@grab_cash", "cart_cash_dissapear", 3) then
 | 
						||
                return
 | 
						||
            end
 | 
						||
        
 | 
						||
            while not NetworkHasControlOfEntity(trolly) do
 | 
						||
                Citizen.Wait(1)
 | 
						||
                NetworkRequestControlOfEntity(trolly)
 | 
						||
            end
 | 
						||
        
 | 
						||
            local bagObj = CreateObject("hei_p_m_bag_var22_arm_s", vector3(0.0, 0.0, 0.0), true)
 | 
						||
            SetEntityCollision(bagObj, false, true)
 | 
						||
        
 | 
						||
            -- Intro
 | 
						||
                local introScene = StartLootIntroScene(bagObj, trolly)
 | 
						||
                developer("^3Scenes^0", "Started Intro scene")
 | 
						||
        
 | 
						||
                SetPedComponentVariation(ped, 5, 0, 0, 0)
 | 
						||
                Citizen.Wait(1500)
 | 
						||
        
 | 
						||
                developer("^3Scenes^0", "Create cash prop")
 | 
						||
                CreateCashProp(id, cashPropModel, options.giveCash)
 | 
						||
                developer("^3Scenes^0", "Starting grabbing scene")
 | 
						||
        
 | 
						||
            -- Grab Scene
 | 
						||
                local grabScene = StartLootGrabScene(bagObj, trolly)
 | 
						||
                developer("^3Scenes^0", "Started grab scene")
 | 
						||
                StartPlayerInteractionGrabLoop(grabScene, options.minSpeed or 1.0, options.maxSpeed or 1.6, options.repeatedlyPress)
 | 
						||
        
 | 
						||
                CollectCashProp(id, options.giveCash) -- last cash prop isnt in the animation events
 | 
						||
                
 | 
						||
                SetEntityModel(trolly, emptyTrolly)
 | 
						||
        
 | 
						||
            -- Exit
 | 
						||
                Utility.Cache.LootingTrolly = false
 | 
						||
        
 | 
						||
                local exitScene = StartLootExitScene(bagObj, trolly)
 | 
						||
                developer("^3Scenes^0", "Started exit scene", trolly)
 | 
						||
                Citizen.Wait(1800)
 | 
						||
        
 | 
						||
                DeleteEntity(bagObj)
 | 
						||
        
 | 
						||
                StopScene(introScene)
 | 
						||
                StopScene(grabScene)
 | 
						||
                StopScene(exitScene)
 | 
						||
                developer("^3Scenes^0", "Stopped all scenes", trolly)
 | 
						||
        
 | 
						||
                SetPedComponentVariation(ped, 5, 45, 0, 0)
 | 
						||
        end
 | 
						||
 | 
						||
    -- Guards
 | 
						||
        local GuardAlertnessLoopRunning = false
 | 
						||
        local SpottedByGuards = false
 | 
						||
 | 
						||
        Citizen.CreateThread(function()
 | 
						||
            AddRelationshipGroup("GUARDS")
 | 
						||
            SetPedRelationshipGroupHash(PlayerPedId(), GetHashKey("PLAYER"))
 | 
						||
        end)
 | 
						||
 | 
						||
        local CheckIfCanAttack = function(player, v)
 | 
						||
            if HasEntityClearLosToEntity(v, player, 27) and GetPedTaskCombatTarget(v) ~= player then
 | 
						||
                TaskCombatHatedTargetsAroundPed(v, 10.0, 0)
 | 
						||
                SetRelationshipBetweenGroups(5, GetHashKey("GUARDS"), GetHashKey("PLAYER"))
 | 
						||
                SetPedToInformRespectedFriends(v, 30.0, 3)
 | 
						||
                SetPedAiBlipHasCone(v, false)
 | 
						||
 | 
						||
                if not SpottedByGuards then
 | 
						||
                    SpottedByGuards = true
 | 
						||
                    TriggerEvent("Utility:On:spotted", v)
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end
 | 
						||
 | 
						||
        local TryToStartGuardAlertnessLoop = function()
 | 
						||
            if not GuardAlertnessLoopRunning then
 | 
						||
                GuardAlertnessLoopRunning = true
 | 
						||
 | 
						||
                Citizen.CreateThread(function()
 | 
						||
                    while GuardAlertnessLoopRunning do        
 | 
						||
                        if next(Utility.Cache.Guards) then -- if there's any guard
 | 
						||
                            local player = PlayerPedId()
 | 
						||
                            local coords = GetEntityCoords(player)
 | 
						||
                            local inStealth = GetPedStealthMovement(player)
 | 
						||
                            local distance = inStealth and 30.0 or 60.0 -- (if stealth then 30.0 else 60.0)
 | 
						||
                            local running = IsPedRunning(player)
 | 
						||
                    
 | 
						||
                            for k,v in ipairs(Utility.Cache.Guards) do
 | 
						||
                                local guardCoords = GetEntityCoords(v)
 | 
						||
        
 | 
						||
                                -- Check if is dying
 | 
						||
                                if IsPedDeadOrDying(v) then
 | 
						||
                                    SetPedCanRagdoll(v, true)
 | 
						||
                                    SetEntityAsNoLongerNeeded(v)
 | 
						||
        
 | 
						||
                                    table.remove(Utility.Cache.Guards, k)
 | 
						||
                                else
 | 
						||
                                    -- Check if to near
 | 
						||
                                    if #(coords - guardCoords) < (running and 8.0 or 5.0) then -- if to near
 | 
						||
                                        CheckIfCanAttack(player, v)
 | 
						||
                                    end
 | 
						||
                                    
 | 
						||
                                    -- Check if can be viewed
 | 
						||
                                    if #(coords - guardCoords) < distance then -- if is in the possible cone
 | 
						||
                                        local guardMaxCoords = GetOffsetFromEntityInWorldCoords(v, 0.0, distance, 0.0)
 | 
						||
                        
 | 
						||
                                        if IsEntityInAngledArea(PlayerPedId(), guardCoords, guardMaxCoords, 50.0) then
 | 
						||
                                            CheckIfCanAttack(player, v)
 | 
						||
                                        end
 | 
						||
                                    end
 | 
						||
    
 | 
						||
                                    if IsPedShooting(v) or IsPedInCombat(v) then
 | 
						||
                                        SetPedToInformRespectedFriends(v, 30.0, 3)
 | 
						||
                                        SetPedAiBlipHasCone(v, false)
 | 
						||
 | 
						||
                                        if not SpottedByGuards then
 | 
						||
                                            SpottedByGuards = true
 | 
						||
                                            TriggerEvent("Utility:On:spotted", v)
 | 
						||
                                        end
 | 
						||
                                    end
 | 
						||
                                end
 | 
						||
    
 | 
						||
                            end
 | 
						||
                        else
 | 
						||
                            SpottedByGuards = false
 | 
						||
                            GuardAlertnessLoopRunning = false
 | 
						||
                        end
 | 
						||
                
 | 
						||
                        Citizen.Wait(500)
 | 
						||
                    end
 | 
						||
                end)
 | 
						||
            end
 | 
						||
        end
 | 
						||
        
 | 
						||
        SetGuardDifficulty = function(guard, difficulty)
 | 
						||
            local armour, alertness, accuracy, range, ability = 0, 0, 0, 0, 0
 | 
						||
 | 
						||
            if difficulty == "easy" then
 | 
						||
                alertness = 1
 | 
						||
                accuracy = 40
 | 
						||
                range = 0
 | 
						||
                ability = 0
 | 
						||
            elseif difficulty == "medium" then
 | 
						||
                alertness = 2
 | 
						||
                accuracy = 60
 | 
						||
                range = 2
 | 
						||
                ability = 1
 | 
						||
            elseif difficulty == "hard" then
 | 
						||
                alertness = 3
 | 
						||
                accuracy = 80
 | 
						||
                range = 2
 | 
						||
                ability = 2
 | 
						||
                armour = 50
 | 
						||
            elseif difficulty == "veryhard" then
 | 
						||
                alertness = 3
 | 
						||
                accuracy = 95
 | 
						||
                range = 2
 | 
						||
                ability = 2
 | 
						||
                armour = 100
 | 
						||
            end
 | 
						||
            
 | 
						||
            SetPedArmour(ped, armour)
 | 
						||
            SetPedAlertness(ped, alertness)
 | 
						||
            SetPedAccuracy(ped, accuracy)
 | 
						||
            SetPedCombatRange(ped, range)
 | 
						||
            SetPedCombatAbility(ped, ability)
 | 
						||
        end
 | 
						||
 | 
						||
        CreateGuard = function(model, coords, heading, difficulty, guardRoute)
 | 
						||
            local ped, netId = CreatePed(model, coords, heading, true)
 | 
						||
            SetPedAiBlip(ped, true)
 | 
						||
            SetPedAiBlipForcedOn(ped, true)
 | 
						||
            SetPedAiBlipHasCone(ped, true)
 | 
						||
        
 | 
						||
            SetPedRandomComponentVariation(ped, 0)
 | 
						||
            SetPedRandomProps(ped)
 | 
						||
            SetPedCanRagdoll(ped, false)
 | 
						||
        
 | 
						||
            --SetEntityAsMissionEntity(ped)
 | 
						||
        
 | 
						||
            SetPedCombatMovement(ped, 2)
 | 
						||
            SetGuardDifficulty(ped, difficulty)
 | 
						||
            
 | 
						||
            SetPedCombatAttributes(ped, 46, true)
 | 
						||
            SetPedFleeAttributes(ped, 0, false)
 | 
						||
 | 
						||
            --GiveWeaponToPed(ped, `WEAPON_PISTOL`, 255, false, true)
 | 
						||
            SetPedRelationshipGroupHash(ped, GetHashKey("GUARDS"))
 | 
						||
        
 | 
						||
            if guardRoute then
 | 
						||
                TaskPatrol(ped, "miss_"..guardRoute, 1, 0, 1)
 | 
						||
            end
 | 
						||
        
 | 
						||
            table.insert(Utility.Cache.Guards, ped)
 | 
						||
 | 
						||
            TryToStartGuardAlertnessLoop()
 | 
						||
            return ped
 | 
						||
        end
 | 
						||
        
 | 
						||
        CreateGuardRoute = function(name, positions, manualRouteLink)
 | 
						||
            OpenPatrolRoute("miss_"..name)
 | 
						||
            local debugLines = {}
 | 
						||
        
 | 
						||
            for i=1, #positions do
 | 
						||
                local position = positions[i]
 | 
						||
        
 | 
						||
                if type(position) == "vector3" then
 | 
						||
                    AddPatrolRouteNode(i, "StandGuard", position, position, 5000)
 | 
						||
                else
 | 
						||
                    AddPatrolRouteNode(i, position.anim or "StandGuard", position.destination, position.viewat or position.destination, position.wait or 5000)
 | 
						||
                end
 | 
						||
        
 | 
						||
                if manualRouteLink then
 | 
						||
                    manualRouteLink(i-1, i)
 | 
						||
                else
 | 
						||
                    if i == #positions then
 | 
						||
                        AddPatrolRouteLink(i, 1) -- close the circle
 | 
						||
                        table.insert(debugLines, {positions[i], positions[1]})
 | 
						||
                    end
 | 
						||
        
 | 
						||
                    if i > 1 then
 | 
						||
                        AddPatrolRouteLink(i-1, i)
 | 
						||
                        table.insert(debugLines, {positions[i-1], positions[i]})
 | 
						||
                    end
 | 
						||
                end
 | 
						||
            end
 | 
						||
        
 | 
						||
            ClosePatrolRoute()
 | 
						||
            CreatePatrolRoute()
 | 
						||
        
 | 
						||
            if DevModeStatus then
 | 
						||
                Citizen.CreateThread(function()
 | 
						||
                    while true do
 | 
						||
                        for i=1, #debugLines do
 | 
						||
                            DrawLine(debugLines[i][1], debugLines[i][2], 255, 0, 0, 255)
 | 
						||
                        end
 | 
						||
        
 | 
						||
                        Citizen.Wait(0)
 | 
						||
                    end
 | 
						||
                end)
 | 
						||
            end
 | 
						||
        end
 | 
						||
        
 | 
						||
        SetGuardRoute = function(guard, route)
 | 
						||
            TaskPatrol(guard, "miss_"..route, 1, 0, 1)
 | 
						||
        end
 | 
						||
 | 
						||
--// Other //--
 | 
						||
    SetEntityModel = function(entity, model)
 | 
						||
        TriggerServerEvent("Utility:SwapModel", GetEntityCoords(entity), GetEntityModel(entity), type(model) == "string" and GetHashKey(model) or model)
 | 
						||
    end
 | 
						||
 | 
						||
    StopCurrentTaskAndWatchPlayer = function(ped, duration)
 | 
						||
        local coords1 = GetEntityCoords(ped, true)
 | 
						||
        local coords2 = GetEntityCoords(PlayerPedId(), true)
 | 
						||
        local heading = GetHeadingFromVector_2d(coords2.x - coords1.x, coords2.y - coords1.y)
 | 
						||
 | 
						||
        TaskAchieveHeading(ped, heading, duration or 2000)
 | 
						||
    end
 | 
						||
 | 
						||
    StartParticleFxOnNetworkEntity = function(ptxAsset, name, obj, ...)
 | 
						||
        TriggerServerEvent("Utility:StartParticleFxOnNetworkEntity", ptxAsset, name, ObjToNet(obj), ...)
 | 
						||
    end
 | 
						||
 | 
						||
    GetEntitySize = function(entity)
 | 
						||
        local model = GetEntityModel(entity)
 | 
						||
        local min, max = GetModelDimensions(model)
 | 
						||
        return max - min
 | 
						||
    end
 | 
						||
 | 
						||
    DebugCoords = function(coords)
 | 
						||
        Citizen.CreateThread(function()
 | 
						||
            while true do
 | 
						||
                DrawText3Ds(coords, "V")
 | 
						||
                Citizen.Wait(0)
 | 
						||
            end
 | 
						||
        end)
 | 
						||
    end
 | 
						||
    
 | 
						||
    GetDirectionFromVectors = function(vec, vec2)
 | 
						||
        return vec - vec2
 | 
						||
    end
 | 
						||
 | 
						||
    RotationToDirection = function(rotation)
 | 
						||
    
 | 
						||
        local adjustedRotation = 
 | 
						||
        { 
 | 
						||
            x = (math.pi / 180) * rotation.x, 
 | 
						||
            y = (math.pi / 180) * rotation.y, 
 | 
						||
            z = (math.pi / 180) * rotation.z 
 | 
						||
        }
 | 
						||
        local direction = 
 | 
						||
        {
 | 
						||
            x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)), 
 | 
						||
            y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)), 
 | 
						||
            z = math.sin(adjustedRotation.x)
 | 
						||
        }
 | 
						||
        return vector3(direction.x, direction.y, direction.z)
 | 
						||
    end
 | 
						||
 | 
						||
    SetVehicleWheelsPowered = function(veh, active)
 | 
						||
        for i=0, GetVehicleNumberOfWheels(veh) - 1 do
 | 
						||
            SetVehicleWheelIsPowered(veh, i, active)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    apairs = function(t, f)
 | 
						||
        local a = {}
 | 
						||
        local i = 0
 | 
						||
    
 | 
						||
        for k in pairs(t) do table.insert(a, k) end
 | 
						||
        table.sort(a, f)
 | 
						||
        
 | 
						||
        local iter = function() -- iterator function
 | 
						||
            i = i + 1
 | 
						||
            if a[i] == nil then 
 | 
						||
                return nil
 | 
						||
            else 
 | 
						||
                return a[i], t[a[i]]
 | 
						||
            end
 | 
						||
        end
 | 
						||
    
 | 
						||
        return iter
 | 
						||
    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
 | 
						||
 | 
						||
    CreateMissionText = function(msg, duration)            
 | 
						||
        SetTextEntry_2("STRING")
 | 
						||
        AddTextComponentString(msg)
 | 
						||
        DrawSubtitleTimed(duration and math.floor(duration) or 60000 * 240, 1) -- 4h
 | 
						||
 | 
						||
        return {
 | 
						||
            delete = function()
 | 
						||
                ClearPrints()
 | 
						||
            end
 | 
						||
        }
 | 
						||
    end
 | 
						||
 | 
						||
    WaitNear = function(coords)
 | 
						||
        local player = PlayerPedId()
 | 
						||
 | 
						||
        while #(GetEntityCoords(player) - coords) > 10 do 
 | 
						||
            Citizen.Wait(100) 
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    FindInTable = function(table, text)
 | 
						||
        for i=1, #table do
 | 
						||
            if table[i] == text then
 | 
						||
                return i
 | 
						||
            end
 | 
						||
        end
 | 
						||
    
 | 
						||
        return nil
 | 
						||
    end
 | 
						||
 | 
						||
    GetRandom = function(table)
 | 
						||
        local random = math.random(1, #table)
 | 
						||
        return table[random]
 | 
						||
    end
 | 
						||
 | 
						||
    Probability = function(number) 
 | 
						||
        return math.random(1, 100) <= number
 | 
						||
    end
 | 
						||
    
 | 
						||
    AddPercentage = function(number, percentage)
 | 
						||
        return number + (number * percentage / 100)
 | 
						||
    end
 | 
						||
    
 | 
						||
    RemovePercentage = function(number, percentage)
 | 
						||
        return number - (number * percentage / 100)
 | 
						||
    end
 | 
						||
 | 
						||
    InTimeRange = function(min, max)
 | 
						||
        local hour = nil
 | 
						||
 | 
						||
        if utc then
 | 
						||
            local _, _, _, _hour = GetUtcTime()
 | 
						||
            hour = _hour
 | 
						||
        else
 | 
						||
            hour = GetClockHours()
 | 
						||
        end
 | 
						||
 | 
						||
        if max > min then
 | 
						||
            if hour >= min and hour <= max then
 | 
						||
                return true
 | 
						||
            end 
 | 
						||
        else
 | 
						||
            -- to fix the times from one day to another, for example from 22 to 3
 | 
						||
            if hour <= max or hour >= min then
 | 
						||
                return false
 | 
						||
            end
 | 
						||
        end
 | 
						||
    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
 | 
						||
 | 
						||
    GetInteriorPositionAndRotation = function(interior)
 | 
						||
        local pos = vec3(GetInteriorPosition(interior))
 | 
						||
 | 
						||
        local rot = vec4(GetInteriorRotation(interior))
 | 
						||
        rot = quat2euler(rot)
 | 
						||
 | 
						||
        return pos, rot
 | 
						||
    end
 | 
						||
 | 
						||
    GetOffsetFromInteriorInWorldCoords = function(interior, offset)
 | 
						||
        local pos, rot = GetInteriorPositionAndRotation(interior)
 | 
						||
        return GetOffsetFromPositionInWorldCoords(pos, rot, offset)
 | 
						||
    end
 | 
						||
 | 
						||
--// UtilityNet //
 | 
						||
local CreatedEntities = {}
 | 
						||
 | 
						||
--#region API
 | 
						||
UtilityNet.ForEachEntity = function(fn, slices)
 | 
						||
    if slices then
 | 
						||
        local entities = GlobalState.Entities
 | 
						||
 | 
						||
        for i = 1, #slices do
 | 
						||
            local _entities = entities[slices[i]]
 | 
						||
            local n = 0
 | 
						||
            
 | 
						||
            if _entities then
 | 
						||
                -- Manual pairs loop for performance
 | 
						||
                local k,v = next(_entities)
 | 
						||
 | 
						||
                while k do
 | 
						||
                    n = n + 1
 | 
						||
                    local ret = fn(v, k)
 | 
						||
        
 | 
						||
                    if ret ~= nil then
 | 
						||
                        return ret
 | 
						||
                    end
 | 
						||
                    k,v = next(_entities, k)
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end
 | 
						||
    else
 | 
						||
        local entities = GlobalState.Entities
 | 
						||
 | 
						||
        if not entities then
 | 
						||
            return
 | 
						||
        end
 | 
						||
 | 
						||
        -- Manual pairs loop for performance
 | 
						||
        local sliceI,slice = next(entities)
 | 
						||
 | 
						||
        while sliceI do
 | 
						||
            local k2, v = next(slice)
 | 
						||
            while k2 do
 | 
						||
                local ret = fn(v, k2)
 | 
						||
 | 
						||
                if ret ~= nil then
 | 
						||
                    return ret
 | 
						||
                end
 | 
						||
 | 
						||
                k2,v = next(slice, k2)
 | 
						||
            end
 | 
						||
 | 
						||
            sliceI, slice = next(entities, sliceI)
 | 
						||
        end
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.InternalFindFromNetId = function(uNetId)
 | 
						||
    for sliceI, slice in pairs(GlobalState.Entities) do
 | 
						||
        if slice[uNetId] then
 | 
						||
            return slice[uNetId], sliceI
 | 
						||
        end
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.SetDebug = function(state)
 | 
						||
    UtilityNetDebug = state
 | 
						||
 | 
						||
    local localEntities = {}
 | 
						||
    Citizen.CreateThread(function()
 | 
						||
        while UtilityNetDebug do
 | 
						||
            localEntities = {}
 | 
						||
            
 | 
						||
            UtilityNet.ForEachEntity(function(v)
 | 
						||
                if v.createdBy == GetCurrentResourceName() then
 | 
						||
                    local obj = UtilityNet.GetEntityFromUNetId(v.id)
 | 
						||
 | 
						||
                    if DoesEntityExist(obj) then
 | 
						||
                        table.insert(localEntities, {
 | 
						||
                            obj = obj,
 | 
						||
                            netId = v.id
 | 
						||
                        })
 | 
						||
                    end
 | 
						||
                end
 | 
						||
            end)
 | 
						||
            Citizen.Wait(3000)
 | 
						||
        end
 | 
						||
    end)
 | 
						||
    Citizen.CreateThread(function()
 | 
						||
        while UtilityNetDebug do
 | 
						||
            for k,v in pairs(localEntities) do
 | 
						||
                local state = UtilityNet.State(v.netId)
 | 
						||
 | 
						||
                DrawText3Ds(GetEntityCoords(v.obj), "NetId: "..v.netId, 0.25)
 | 
						||
            end
 | 
						||
            Citizen.Wait(1)
 | 
						||
        end
 | 
						||
    end)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.SetModelRenderDistance = function(model, distance)
 | 
						||
    TriggerServerEvent("Utility:Net:SetModelRenderDistance", model, distance)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.CreateEntity = function(model, coords, options)
 | 
						||
    if type(model) ~= "string" then
 | 
						||
        error("Invalid model, got "..type(model).." expected string", 0)
 | 
						||
    end
 | 
						||
 | 
						||
    -- Set resource name in options
 | 
						||
    options = options or {}
 | 
						||
    options.resource = GetCurrentResourceName()
 | 
						||
 | 
						||
    local callId = math.random(0, 10000000)
 | 
						||
    local event = nil
 | 
						||
    local entity = promise:new()
 | 
						||
 | 
						||
    -- Callback
 | 
						||
    event = RegisterNetEvent("Utility:Net:EntityCreated", function(_callId, uNetId)
 | 
						||
        if _callId == callId then
 | 
						||
            entity:resolve(uNetId)
 | 
						||
            RemoveEventHandler(event)
 | 
						||
        end
 | 
						||
    end)
 | 
						||
 | 
						||
    TriggerLatentServerEvent("Utility:Net:CreateEntity", 5120, callId, model, coords, options)
 | 
						||
    local id = Citizen.Await(entity) -- Wait for server response
 | 
						||
    table.insert(CreatedEntities, id)
 | 
						||
 | 
						||
    return id
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.DoesEntityExist = function(uNetId)
 | 
						||
    return DoesEntityExist(UtilityNet.GetEntityFromUNetId(uNetId))
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.GetClosestRenderedNetIdOfType = function(coords, radius, model)
 | 
						||
    local entities = exports["utility_lib"]:GetRenderedEntities()
 | 
						||
    local closest = nil
 | 
						||
    local minDist = math.huge
 | 
						||
    
 | 
						||
    for k, v in pairs(entities) do
 | 
						||
        if DoesEntityExist(v.obj) and GetEntityModel(v.obj) == model then
 | 
						||
            local dist = #(coords - GetEntityCoords(v.obj))
 | 
						||
 | 
						||
            if dist < minDist then
 | 
						||
                closest = k
 | 
						||
                minDist = dist
 | 
						||
            end
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    return closest
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.GetClosestRenderedObjectOfType = function(coords, radius, model)
 | 
						||
    local closest = UtilityNet.GetClosestRenderedNetIdOfType(coords, radius, model)
 | 
						||
 | 
						||
    if closest then
 | 
						||
        return UtilityNet.GetEntityFromUNetId(closest)    
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.GetClosestNetIdOfType = function(coords, radius, model)
 | 
						||
    if type(model) == "string" then
 | 
						||
        model = GetHashKey(model)
 | 
						||
    end
 | 
						||
 | 
						||
    local closest = nil
 | 
						||
    local minDist = math.huge
 | 
						||
    local slice = GetSliceFromCoords(coords)
 | 
						||
    local slices = GetSurroundingSlices(slice)
 | 
						||
 | 
						||
    -- Iterate only through near slices to improve performance
 | 
						||
    for k, v in pairs(slices) do
 | 
						||
        UtilityNet.ForEachEntity(function(entity)
 | 
						||
            if entity.model == model then
 | 
						||
                local distance = #(coords - entity.coords)
 | 
						||
    
 | 
						||
                if distance < radius and distance < minDist then
 | 
						||
                    minDist = distance
 | 
						||
                    closest = entity.id
 | 
						||
                end
 | 
						||
            end
 | 
						||
        end, {v})
 | 
						||
    end
 | 
						||
 | 
						||
    return closest
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.DeleteEntity = function(uNetId)
 | 
						||
    TriggerServerEvent("Utility:Net:DeleteEntity", uNetId)
 | 
						||
 | 
						||
    for k, v in pairs(CreatedEntities) do
 | 
						||
        if v == uNetId then
 | 
						||
            table.remove(CreatedEntities, k)
 | 
						||
            break
 | 
						||
        end
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.AttachToEntity = function(uNetId, object, bone, pos, rot, useSoftPinning, collision, rotationOrder, syncRot)
 | 
						||
    local params = {bone = bone, pos = pos, rot = rot, useSoftPinning = useSoftPinning, collision = collision, rotationOrder = rotationOrder, syncRot = syncRot}
 | 
						||
 | 
						||
    if DoesEntityExist(object) and NetworkGetEntityIsNetworked(object) then
 | 
						||
        TriggerServerEvent("Utility:Net:AttachToEntity", uNetId, NetworkGetNetworkIdFromEntity(object), params)
 | 
						||
    else
 | 
						||
        params.isUtilityNet = true
 | 
						||
        TriggerServerEvent("Utility:Net:AttachToEntity", uNetId, object, params)
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.DetachEntity = function(uNetId)
 | 
						||
    local obj = UtilityNet.GetEntityFromUNetId(uNetId)
 | 
						||
    local coords = GetEntityCoords(obj)
 | 
						||
 | 
						||
    TriggerServerEvent("Utility:Net:DetachEntity", uNetId, coords)
 | 
						||
 | 
						||
    while IsEntityAttached(obj) do
 | 
						||
        Citizen.Wait(1)
 | 
						||
    end
 | 
						||
 | 
						||
    local state = UtilityNet.State(uNetId)
 | 
						||
    while state.__attached do
 | 
						||
        Citizen.Wait(1)
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
-- Using a latent event to prevent blocking the network channel
 | 
						||
UtilityNet.SetEntityCoords = function(uNetId, coords)
 | 
						||
    TriggerLatentServerEvent("Utility:Net:SetEntityCoords", 5120, uNetId, coords)
 | 
						||
 | 
						||
    -- Instantly sync for local obj
 | 
						||
    TriggerEvent("Utility:Net:RefreshCoords", uNetId, coords)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.SetEntityRotation = function(uNetId, rot)
 | 
						||
    TriggerLatentServerEvent("Utility:Net:SetEntityRotation", 5120, uNetId, rot)
 | 
						||
 | 
						||
    -- Instantly sync for local obj
 | 
						||
    TriggerEvent("Utility:Net:RefreshRotation", uNetId, rot)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.SetEntityModel = function(uNetId, model)
 | 
						||
    TriggerLatentServerEvent("Utility:Net:SetEntityModel", 5120, uNetId, model)
 | 
						||
 | 
						||
    -- Instantly sync for local obj
 | 
						||
    TriggerEvent("Utility:Net:RefreshModel", uNetId, model)
 | 
						||
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.PreserveEntity = function(uNetId)
 | 
						||
    local entity = UtilityNet.GetEntityFromUNetId(uNetId)
 | 
						||
    
 | 
						||
    if entity then
 | 
						||
        Entity(entity).state.preserved = true
 | 
						||
    else
 | 
						||
        warn("PreserverEntity: Entity with uNetId "..uNetId.." not found")        
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.OnRender = function(cb)
 | 
						||
    AddEventHandler("Utility:Net:OnRender", cb)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.OnUnrender = function(cb)
 | 
						||
    AddEventHandler("Utility:Net:OnUnrender", cb)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.IsReady = function(uNetId)
 | 
						||
    local obj = UtilityNet.GetEntityFromUNetId(uNetId)
 | 
						||
 | 
						||
    return DoesEntityExist(obj) and UtilityNet.IsEntityRendered(obj)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.IsEntityRendered = function(obj)
 | 
						||
    local state = Entity(obj).state
 | 
						||
    return state.rendered
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.DoesUNetIdExist = function(uNetId)
 | 
						||
    local entity = UtilityNet.InternalFindFromNetId(uNetId)
 | 
						||
    return entity or false
 | 
						||
end
 | 
						||
 | 
						||
--#region State
 | 
						||
UtilityNet.AddStateBagChangeHandler = function(uNetId, func)
 | 
						||
    return RegisterNetEvent("Utility:Net:UpdateStateValue", function(s_uNetId, key, value)
 | 
						||
        if uNetId == s_uNetId then
 | 
						||
            func(key, value)
 | 
						||
        end
 | 
						||
    end)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.RemoveStateBagChangeHandler = function(eventData)
 | 
						||
    if eventData and eventData.key and eventData.name then
 | 
						||
        RemoveEventHandler(eventData)
 | 
						||
    end
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.State = function(uNetId)
 | 
						||
    local state = setmetatable({}, {
 | 
						||
        __index = function(_, k)
 | 
						||
            return exports["utility_lib"]:GetEntityStateValue(uNetId, k)
 | 
						||
        end,
 | 
						||
 | 
						||
        __newindex = function(_, k, v)
 | 
						||
            error("Cannot set states from client")
 | 
						||
        end
 | 
						||
    })
 | 
						||
 | 
						||
    return state
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.StateFromObj = function(obj)
 | 
						||
    local netId = UtilityNet.GetUNetIdFromEntity(obj)
 | 
						||
 | 
						||
    return UtilityNet.State(netId)
 | 
						||
end
 | 
						||
--#endregion
 | 
						||
 | 
						||
--#region Casters
 | 
						||
UtilityNet.GetEntityFromUNetId = function(uNetId)
 | 
						||
    return exports["utility_lib"]:GetEntityFromUNetId(uNetId)
 | 
						||
end
 | 
						||
 | 
						||
UtilityNet.GetUNetIdFromEntity = function(entity)
 | 
						||
    return exports["utility_lib"]:GetUNetIdFromEntity(entity)
 | 
						||
end
 | 
						||
--#endregion
 | 
						||
 | 
						||
--#endregion
 | 
						||
 | 
						||
--#region Garbage Collection
 | 
						||
AddEventHandler("onResourceStop", function(resource)
 | 
						||
    local currentResource = GetCurrentResourceName()
 | 
						||
 | 
						||
    if resource == currentResource then
 | 
						||
        for k, v in pairs(CreatedEntities) do
 | 
						||
            TriggerServerEvent("Utility:Net:DeleteEntity", v)
 | 
						||
        end
 | 
						||
    end
 | 
						||
end)
 | 
						||
--#endregion
 |