_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 -- Uses table.clone for fast shallow copying (memcpy) before checking and doing actual deepcopy for nested tables -- Handles circular references via seen table -- Significantly faster (~50%) than doing actual deepcopy for flat or lightly-nested structures ---@param orig table ---@return table table.deepcopy = function(orig, seen) if type(orig) ~= "table" then return orig end seen = seen or {} if seen[orig] then return seen[orig] end local copy = table.clone(orig) seen[orig] = copy for k, v in next, orig do if type(v) == "table" then copy[k] = table.deepcopy(v, seen) end end return copy 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 playerCoords = GetEntityCoords(ped) local bagObj = CreateObject("hei_p_m_bag_var22_arm_s", playerCoords + vector3(0.0, 0.0, -6.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 = {} local old_GetEntityArchetypeName = GetEntityArchetypeName GetEntityArchetypeName = function(entity) if not entity or not DoesEntityExist(entity) then return "" end local res = old_GetEntityArchetypeName(entity) if res == "" then return Entity(entity)?.state?.abstract_model or res else return res end end --#region API UtilityNet.ForEachEntity = function(fn, slices) if slices then for i = 1, #slices do local _entities = UtilityNet.GetEntities(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 = UtilityNet.GetEntities() 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.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) if DoesEntityExist(v.obj) then DrawText3Ds(GetEntityCoords(v.obj), "NetId: "..v.netId, 0.25) end 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.createdBy = GetCurrentResourceName() local callId = math.random(0, 10000000) local event = nil local entity = promise:new() -- Callback event = RegisterNetEvent("Utility:Net:EntityCreated", function(_callId, object) if _callId == callId then entity:resolve(object.id) 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, skipPositionUpdate) TriggerLatentServerEvent("Utility:Net:SetEntityCoords", 5120, uNetId, coords) -- Instantly sync for local obj TriggerEvent("Utility:Net:RefreshCoords", uNetId, coords, skipPositionUpdate) end UtilityNet.SetEntityRotation = function(uNetId, rot, skipRotationUpdate) TriggerLatentServerEvent("Utility:Net:SetEntityRotation", 5120, uNetId, rot, skipRotationUpdate) -- Instantly sync for local obj TriggerEvent("Utility:Net:RefreshRotation", uNetId, rot, skipRotationUpdate) 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 UtilityNet.GetuNetIdCreator = function(uNetId) return exports["utility_lib"]:GetuNetIdCreator(uNetId) end UtilityNet.GetEntityCreator = function(entity) return exports["utility_lib"]:GetEntityCreator(entity) end UtilityNet.InternalFindFromNetId = function(uNetId) return exports["utility_lib"]:InternalFindFromNetId(uNetId) end UtilityNet.GetEntities = function(slice) return exports["utility_lib"]:GetEntities(slice) 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