local QBCore = exports['qb-core']:GetCoreObject() local inside = false local currentHouse = nil local closestHouse local inRange local IsLockpicking = false local houseObj = {} local POIOffsets = nil local usingAdvanced = false local requiredItemsShowed = false local requiredItems = {} local CurrentCops = 0 local openingDoor = false local SucceededAttempts = 0 local NeededAttempts = 4 -- Functions local function DrawText3Ds(x, y, z, text) SetTextScale(0.35, 0.35) SetTextFont(4) SetTextProportional(1) SetTextColour(255, 255, 255, 215) SetTextEntry("STRING") SetTextCentre(true) AddTextComponentString(text) SetDrawOrigin(x,y,z, 0) DrawText(0.0, 0.0) local factor = (string.len(text)) / 370 DrawRect(0.0, 0.0+0.0125, 0.017+ factor, 0.03, 0, 0, 0, 75) ClearDrawOrigin() end local function loadAnimDict(dict) while (not HasAnimDictLoaded(dict)) do RequestAnimDict(dict) Wait(5) end end local function openHouseAnim() loadAnimDict("anim@heists@keycard@") TaskPlayAnim( PlayerPedId(), "anim@heists@keycard@", "exit", 5.0, 1.0, -1, 16, 0, 0, 0, 0 ) Wait(400) ClearPedTasks(PlayerPedId()) end local function enterRobberyHouse(house) TriggerServerEvent("InteractSound_SV:PlayOnSource", "houses_door_open", 0.25) openHouseAnim() Wait(250) local coords = { x = Config.Houses[house]["coords"]["x"], y = Config.Houses[house]["coords"]["y"], z= Config.Houses[house]["coords"]["z"] - Config.MinZOffset} local data if Config.Houses[house]["tier"] == 1 then data = exports['qb-interior']:CreateHouseRobbery(coords) elseif Config.Houses[house]["tier"] == 2 then data = exports['qb-interior']:CreateHouseRobbery(coords) elseif Config.Houses[house]["tier"] == 3 then data = exports['qb-interior']:CreateHouseRobbery(coords) end Wait(100) houseObj = data[1] POIOffsets = data[2] inside = true currentHouse = house Wait(500) TriggerEvent('qb-weathersync:client:DisableSync') end local function leaveRobberyHouse(house) local ped = PlayerPedId() TriggerServerEvent("InteractSound_SV:PlayOnSource", "houses_door_open", 0.25) openHouseAnim() Wait(250) DoScreenFadeOut(250) Wait(500) exports['qb-interior']:DespawnInterior(houseObj, function() TriggerEvent('qb-weathersync:client:EnableSync') Wait(250) DoScreenFadeIn(250) SetEntityCoords(ped, Config.Houses[house]["coords"]["x"], Config.Houses[house]["coords"]["y"], Config.Houses[house]["coords"]["z"] + 0.5) SetEntityHeading(ped, Config.Houses[house]["coords"]["h"]) inside = false currentHouse = nil end) end local function PoliceCall() local chance = 75 if GetClockHours() >= 1 and GetClockHours() <= 6 then chance = 25 end if math.random(1, 150) <= chance then if Config.PoliceNotifyType == 'qb' then TriggerServerEvent('police:server:policeAlert', Lang:t("info.palert")) elseif Config.PoliceNotifyType == 'ps' then exports['ps-dispatch']:HouseRobbery() end end end local function lockpickFinish(success) if success then TriggerServerEvent('qb-houserobbery:server:enterHouse', closestHouse) QBCore.Functions.Notify(Lang:t("success.worked"), "success", 2500) else if usingAdvanced then if math.random(1, 100) < 20 then TriggerServerEvent("qb-houserobbery:server:removeAdvancedLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["advancedlockpick"], "remove") end else if math.random(1, 100) < 40 then TriggerServerEvent("qb-houserobbery:server:removeLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["lockpick"], "remove") end end QBCore.Functions.Notify(Lang:t("error.didnt_work"), "error", 2500) end end local function LockpickDoorAnim() openingDoor = true CreateThread(function() while true do if openingDoor then TaskPlayAnim(PlayerPedId(), "veh@break_in@0h@p_m_one@", "low_force_entry_ds", 3.0, 3.0, -1, 16, 0, 0, 0, 0) else StopAnimTask(PlayerPedId(), "veh@break_in@0h@p_m_one@", "low_force_entry_ds", 1.0) break end Wait(1000) end end) end local function IsWearingGloves() local armIndex = GetPedDrawableVariation(PlayerPedId(), 3) local model = GetEntityModel(PlayerPedId()) local retval = true if model == `mp_m_freemode_01` then if Config.MaleNoGloves[armIndex] ~= nil and Config.MaleNoGloves[armIndex] then retval = false end else if Config.FemaleNoGloves[armIndex] ~= nil and Config.FemaleNoGloves[armIndex] then retval = false end end return retval end local function searchCabin(cabin) local ped = PlayerPedId() local Skillbar = exports['qb-skillbar']:GetSkillbarObject() if math.random(1, 100) <= 85 and not IsWearingGloves() then local pos = GetEntityCoords(PlayerPedId()) TriggerServerEvent("evidence:server:CreateFingerDrop", pos) end TriggerEvent('animations:client:EmoteCommandStart', {"kneel"}) TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, true) FreezeEntityPosition(ped, true) IsLockpicking = true if Config.MiniGameSettings.Searching.Type == 'skillbar' then Skillbar.Start({ duration = Config.MiniGameSettings.Searching.SkillBar.Start.SkillBarDuration, pos = Config.MiniGameSettings.Searching.SkillBar.Start.SkillBarPosition, width = Config.MiniGameSettings.Searching.SkillBar.Start.SkillBarWidth, }, function() if SucceededAttempts + 1 >= NeededAttempts then openingDoor = false ClearPedTasks(PlayerPedId()) TriggerServerEvent('qb-houserobbery:server:searchCabin', cabin, currentHouse) Config.Houses[currentHouse]["furniture"][cabin]["searched"] = true TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, false) SucceededAttempts = 0 FreezeEntityPosition(ped, false) SetTimeout(500, function() IsLockpicking = false end) else Skillbar.Repeat({ duration = Config.MiniGameSettings.Searching.SkillBar.Repeat.SkillBarDuration, pos = Config.MiniGameSettings.Searching.SkillBar.Repeat.SkillBarPosition, width = Config.MiniGameSettings.Searching.SkillBar.Repeat.SkillBarWidth, }) SucceededAttempts = SucceededAttempts + 1 end end, function() openingDoor = false ClearPedTasks(PlayerPedId()) TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, false) QBCore.Functions.Notify(Lang:t("error.process_cancelled"), "error", 3500) SucceededAttempts = 0 FreezeEntityPosition(ped, false) SetTimeout(500, function() IsLockpicking = false end) end) elseif Config.MiniGameSettings.Searching.Type == 'ps-ui' then exports['ps-ui']:Circle(function(success) if success then openingDoor = false ClearPedTasks(PlayerPedId()) TriggerServerEvent('qb-houserobbery:server:searchCabin', cabin, currentHouse) Config.Houses[currentHouse]["furniture"][cabin]["searched"] = true TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, false) FreezeEntityPosition(ped, false) SetTimeout(500, function() IsLockpicking = false end) TriggerEvent('animations:client:EmoteCommandStart', {"c"}) else print("fail") TriggerEvent('animations:client:EmoteCommandStart', {"c"}) openingDoor = false ClearPedTasks(PlayerPedId()) TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, false) QBCore.Functions.Notify(Lang:t("error.process_cancelled"), "error", 3500) FreezeEntityPosition(ped, false) SetTimeout(500, function() IsLockpicking = false end) end end, Config.MiniGameSettings.Searching.SkillCircle.Circles, Config.MiniGameSettings.Searching.SkillCircle.Time) elseif Config.MiniGameSettings.Searching.Type == 'keydrop' then exports['boii_ui']:keydrop(Config.MiniGameSettings.Searching.KeyDrop.ScoreLimit, Config.MiniGameSettings.Searching.KeyDrop.MissLimit, Config.MiniGameSettings.Searching.KeyDrop.FallDelay, Config.MiniGameSettings.Searching.KeyDrop.NewLetterDelay, function(success) --[[callback]] if success then print('keydrop success') openingDoor = false ClearPedTasks(PlayerPedId()) TriggerServerEvent('qb-houserobbery:server:searchCabin', cabin, currentHouse) Config.Houses[currentHouse]["furniture"][cabin]["searched"] = true TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, false) FreezeEntityPosition(ped, false) SetTimeout(500, function() IsLockpicking = false end) TriggerEvent('animations:client:EmoteCommandStart', {"c"}) else print('keydrop failed') TriggerEvent('animations:client:EmoteCommandStart', {"c"}) openingDoor = false ClearPedTasks(PlayerPedId()) TriggerServerEvent('qb-houserobbery:server:SetBusyState', cabin, currentHouse, false) QBCore.Functions.Notify(Lang:t("error.process_cancelled"), "error", 3500) FreezeEntityPosition(ped, false) SetTimeout(500, function() IsLockpicking = false end) end end) end end -- Events RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() QBCore.Functions.TriggerCallback('qb-houserobbery:server:GetHouseConfig', function(HouseConfig) Config.Houses = HouseConfig end) end) RegisterNetEvent('qb-houserobbery:client:ResetHouseState', function(house) Config.Houses[house]["opened"] = false for _, v in pairs(Config.Houses[house]["furniture"]) do v["searched"] = false end end) RegisterNetEvent('police:SetCopCount', function(amount) CurrentCops = amount end) RegisterNetEvent('qb-houserobbery:client:enterHouse', function(house) enterRobberyHouse(house) end) RegisterNetEvent('qb-houserobbery:client:setHouseState', function(house, state) Config.Houses[house]["opened"] = state end) RegisterNetEvent('qb-houserobbery:client:setCabinState', function(house, cabin, state) Config.Houses[house]["furniture"][cabin]["searched"] = state end) RegisterNetEvent('qb-houserobbery:client:SetBusyState', function(cabin, house, bool) Config.Houses[house]["furniture"][cabin]["isBusy"] = bool end) RegisterNetEvent('lockpicks:UseLockpick', function(isAdvanced) local hours = GetClockHours() if hours >= Config.MinimumTime or hours <= Config.MaximumTime then usingAdvanced = isAdvanced if usingAdvanced then if closestHouse ~= nil then if CurrentCops >= Config.MinimumHouseRobberyPolice then if not Config.Houses[closestHouse]["opened"] then PoliceCall() TriggerEvent('animations:client:EmoteCommandStart', {"kneel"}) if Config.MiniGameSettings.LockPicking.Type == 'lockpick' then LockpickDoorAnim() TriggerEvent('qb-lockpick:client:openLockpick', lockpickFinish) elseif Config.MiniGameSettings.LockPicking.Type == 'ps-ui' then exports['ps-ui']:Circle(function(success) if success then TriggerEvent('animations:client:EmoteCommandStart', {"c"}) print("success") LockpickDoorAnim() TriggerServerEvent('qb-houserobbery:server:enterHouse', closestHouse) QBCore.Functions.Notify(Lang:t("success.worked"), "success", 2500) if usingAdvanced then if math.random(1, 100) < 20 then TriggerServerEvent("qb-houserobbery:server:removeAdvancedLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["advancedlockpick"], "remove") end else if math.random(1, 100) < 40 then TriggerServerEvent("qb-houserobbery:server:removeLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["lockpick"], "remove") end end else TriggerEvent('animations:client:EmoteCommandStart', {"c"}) QBCore.Functions.Notify(Lang:t("error.didnt_work"), "error", 2500) print("fail") end end, Config.MiniGameSettings.LockPicking.SkillCircle.Circles, Config.MiniGameSettings.LockPicking.SkillCircle.Time) elseif Config.MiniGameSettings.LockPicking.Type == 'keydrop' then exports['boii_ui']:keydrop(Config.MiniGameSettings.LockPicking.KeyDrop.ScoreLimit, Config.MiniGameSettings.LockPicking.KeyDrop.MissLimit, Config.MiniGameSettings.LockPicking.KeyDrop.FallDelay, Config.MiniGameSettings.LockPicking.KeyDrop.NewLetterDelay, function(success) --[[callback]] if success then TriggerEvent('animations:client:EmoteCommandStart', {"c"}) print("success") LockpickDoorAnim() TriggerServerEvent('qb-houserobbery:server:enterHouse', closestHouse) QBCore.Functions.Notify(Lang:t("success.worked"), "success", 2500) if usingAdvanced then if math.random(1, 100) < 20 then TriggerServerEvent("qb-houserobbery:server:removeAdvancedLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["advancedlockpick"], "remove") end else if math.random(1, 100) < 40 then TriggerServerEvent("qb-houserobbery:server:removeLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["lockpick"], "remove") end end else TriggerEvent('animations:client:EmoteCommandStart', {"c"}) QBCore.Functions.Notify(Lang:t("error.didnt_work"), "error", 2500) print("fail") end end) elseif Config.MiniGameSettings.LockPicking.Type == 't3' then local success = exports["t3_lockpick"]:startLockpick(1.0, Config.MiniGameSettings.LockPicking.T3Lockpick.Difficulty, Config.MiniGameSettings.LockPicking.T3Lockpick.Pins) if success then TriggerEvent('animations:client:EmoteCommandStart', {"c"}) print("success") LockpickDoorAnim() TriggerServerEvent('qb-houserobbery:server:enterHouse', closestHouse) QBCore.Functions.Notify(Lang:t("success.worked"), "success", 2500) if usingAdvanced then if math.random(1, 100) < 20 then TriggerServerEvent("qb-houserobbery:server:removeAdvancedLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["advancedlockpick"], "remove") end else if math.random(1, 100) < 40 then TriggerServerEvent("qb-houserobbery:server:removeLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["lockpick"], "remove") end end else TriggerEvent('animations:client:EmoteCommandStart', {"c"}) QBCore.Functions.Notify(Lang:t("error.didnt_work"), "error", 2500) print("fail") end end if math.random(1, 100) <= 85 and not IsWearingGloves() then local pos = GetEntityCoords(PlayerPedId()) TriggerServerEvent("evidence:server:CreateFingerDrop", pos) end else QBCore.Functions.Notify(Lang:t("error.door_open"), "error", 3500) end else QBCore.Functions.Notify(Lang:t("error.not_enough_police"), "error", 3500) end end else local result = QBCore.Functions.HasItem("screwdriverset") if closestHouse ~= nil then if result then if CurrentCops >= Config.MinimumHouseRobberyPolice then if not Config.Houses[closestHouse]["opened"] then PoliceCall() if Config.MiniGameSettings.LockPicking.Type == 'lockpick' then LockpickDoorAnim() TriggerEvent('qb-lockpick:client:openLockpick', lockpickFinish) elseif Config.MiniGameSettings.LockPicking.Type == 'ps-ui' then exports['ps-ui']:Circle(function(success) if success then TriggerEvent('animations:client:EmoteCommandStart', {"c"}) print("success") LockpickDoorAnim() TriggerServerEvent('qb-houserobbery:server:enterHouse', closestHouse) QBCore.Functions.Notify(Lang:t("success.worked"), "success", 2500) if usingAdvanced then if math.random(1, 100) < 20 then TriggerServerEvent("qb-houserobbery:server:removeAdvancedLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["advancedlockpick"], "remove") end else if math.random(1, 100) < 40 then TriggerServerEvent("qb-houserobbery:server:removeLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["lockpick"], "remove") end end else TriggerEvent('animations:client:EmoteCommandStart', {"c"}) QBCore.Functions.Notify(Lang:t("error.didnt_work"), "error", 2500) print("fail") end end, Config.MiniGameSettings.LockPicking.SkillCircle.Circles, Config.MiniGameSettings.LockPicking.SkillCircle.Time) elseif Config.MiniGameSettings.LockPicking.Type == 'keydrop' then exports['boii_ui']:keydrop(Config.MiniGameSettings.Searching.KeyDrop.ScoreLimit, Config.MiniGameSettings.Searching.KeyDrop.MissLimit, Config.MiniGameSettings.Searching.KeyDrop.FallDelay, Config.MiniGameSettings.Searching.KeyDrop.NewLetterDelay, function(success) --[[callback]] if success then TriggerEvent('animations:client:EmoteCommandStart', {"c"}) print("success") LockpickDoorAnim() TriggerServerEvent('qb-houserobbery:server:enterHouse', closestHouse) QBCore.Functions.Notify(Lang:t("success.worked"), "success", 2500) if usingAdvanced then if math.random(1, 100) < 20 then TriggerServerEvent("qb-houserobbery:server:removeAdvancedLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["advancedlockpick"], "remove") end else if math.random(1, 100) < 40 then TriggerServerEvent("qb-houserobbery:server:removeLockpick") TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["lockpick"], "remove") end end else TriggerEvent('animations:client:EmoteCommandStart', {"c"}) QBCore.Functions.Notify(Lang:t("error.didnt_work"), "error", 2500) print("fail") end end) end if math.random(1, 100) <= 85 and not IsWearingGloves() then local pos = GetEntityCoords(PlayerPedId()) TriggerServerEvent("evidence:server:CreateFingerDrop", pos) end else QBCore.Functions.Notify(Lang:t("error.door_open"), "error", 3500) end else QBCore.Functions.Notify(Lang:t("error.not_enough_police"), "error", 3500) end else QBCore.Functions.Notify(Lang:t("error.missing_something"), "error", 3500) end end end end end) -- Threads CreateThread(function() Wait(500) requiredItems = { [1] = {name = QBCore.Shared.Items["advancedlockpick"]["name"], image = QBCore.Shared.Items["advancedlockpick"]["image"]}, [2] = {name = QBCore.Shared.Items["screwdriverset"]["name"], image = QBCore.Shared.Items["screwdriverset"]["image"]}, } while true do inRange = false local PlayerPed = PlayerPedId() local PlayerPos = GetEntityCoords(PlayerPed) closestHouse = nil if QBCore ~= nil then local hours = GetClockHours() if hours >= Config.MinimumTime or hours <= Config.MaximumTime then if not inside then for k, _ in pairs(Config.Houses) do local dist = #(PlayerPos - vector3(Config.Houses[k]["coords"]["x"], Config.Houses[k]["coords"]["y"], Config.Houses[k]["coords"]["z"])) if dist <= 1.5 then closestHouse = k inRange = true if CurrentCops >= Config.MinimumHouseRobberyPolice then if Config.Houses[k]["opened"] then DrawText3Ds(Config.Houses[k]["coords"]["x"], Config.Houses[k]["coords"]["y"], Config.Houses[k]["coords"]["z"], Lang:t("info.henter")) if IsControlJustPressed(0, 38) then enterRobberyHouse(k) end else if not requiredItemsShowed then requiredItemsShowed = true TriggerEvent('inventory:client:requiredItems', requiredItems, true) end end end end end end end if inside then Wait(1000) end if not inRange then if requiredItemsShowed then requiredItemsShowed = false TriggerEvent('inventory:client:requiredItems', requiredItems, false) end Wait(1000) end end Wait(5) end end) CreateThread(function() while true do local ped = PlayerPedId() local pos = GetEntityCoords(ped) if inside then if #(pos - vector3(Config.Houses[currentHouse]["coords"]["x"] + POIOffsets.exit.x, Config.Houses[currentHouse]["coords"]["y"] + POIOffsets.exit.y, Config.Houses[currentHouse]["coords"]["z"] - Config.MinZOffset + POIOffsets.exit.z)) < 1.5 then DrawText3Ds(Config.Houses[currentHouse]["coords"]["x"] + POIOffsets.exit.x, Config.Houses[currentHouse]["coords"]["y"] + POIOffsets.exit.y, Config.Houses[currentHouse]["coords"]["z"] - Config.MinZOffset + POIOffsets.exit.z, Lang:t("info.hleave")) if IsControlJustPressed(0, 38) then leaveRobberyHouse(currentHouse) end end for k, _ in pairs(Config.Houses[currentHouse]["furniture"]) do if #(pos - vector3(Config.Houses[currentHouse]["coords"]["x"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["x"], Config.Houses[currentHouse]["coords"]["y"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["y"], Config.Houses[currentHouse]["coords"]["z"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["z"] - Config.MinZOffset)) < 1 then if not Config.Houses[currentHouse]["furniture"][k]["searched"] then if not Config.Houses[currentHouse]["furniture"][k]["isBusy"] then DrawText3Ds(Config.Houses[currentHouse]["coords"]["x"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["x"], Config.Houses[currentHouse]["coords"]["y"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["y"], Config.Houses[currentHouse]["coords"]["z"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["z"] - Config.MinZOffset, Lang:t("info.aint")..Config.Houses[currentHouse]["furniture"][k]["text"]) if not IsLockpicking then if IsControlJustReleased(0, 38) then searchCabin(k) end end else DrawText3Ds(Config.Houses[currentHouse]["coords"]["x"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["x"], Config.Houses[currentHouse]["coords"]["y"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["y"], Config.Houses[currentHouse]["coords"]["z"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["z"] - Config.MinZOffset, Lang:t("info.hsearch")) end else DrawText3Ds(Config.Houses[currentHouse]["coords"]["x"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["x"], Config.Houses[currentHouse]["coords"]["y"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["y"], Config.Houses[currentHouse]["coords"]["z"] + Config.Houses[currentHouse]["furniture"][k]["coords"]["z"] - Config.MinZOffset, Lang:t("info.hsempty")) end end end end if not inside then Wait(5000) end Wait(3) end end) -- Util Command (can be commented out - used for setting new spots in the config) RegisterCommand('gethroffset', function() local coords = GetEntityCoords(PlayerPedId()) local houseCoords = vector3( Config.Houses[currentHouse]["coords"]["x"], Config.Houses[currentHouse]["coords"]["y"], Config.Houses[currentHouse]["coords"]["z"] - Config.MinZOffset ) if inside then local xdist = coords.x - houseCoords.x local ydist = coords.y - houseCoords.y local zdist = coords.z - houseCoords.z print('X: '..xdist) print('Y: '..ydist) print('Z: '..zdist) end end, false)