local Entity = Entity local function getDoorFromEntity(data) local entity = type(data) == 'table' and data.entity or data if not entity then return end local state = Entity(entity)?.state local doorId = state?.doorId if not doorId then return end local door = doors[doorId] if not door then state.doorId = nil end return door end exports('getClosestDoorId', function() return ClosestDoor?.id end) exports('getDoorIdFromEntity', function(entityId) return getDoorFromEntity(entityId)?.id end) -- same as Entity(entityId).state.doorId local function entityIsNotDoor(data) local entity = type(data) == 'number' and data or data.entity return not getDoorFromEntity(entity) end PickingLock = false local function canPickLock(entity) if PickingLock then return false end local door = getDoorFromEntity(entity) return door and door.lockpick and (Config.CanPickUnlockedDoors or door.state == 1) end ---@param entity number local function pickLock(entity) local door = getDoorFromEntity(entity) if not door or PickingLock or not door.lockpick or (not Config.CanPickUnlockedDoors and door.state == 0) then return end PickingLock = true TaskTurnPedToFaceCoord(cache.ped, door.coords.x, door.coords.y, door.coords.z, 4000) Wait(500) local animDict = lib.requestAnimDict('mp_common_heist') TaskPlayAnim(cache.ped, animDict, 'pick_door', 3.0, 1.0, -1, 49, 0, true, true, true) local success = lib.skillCheck(door.lockpickDifficulty or Config.LockDifficulty) local rand = math.random(1, success and 100 or 5) if success then TriggerServerEvent('ox_doorlock:setState', door.id, door.state == 1 and 0 or 1, true) end if rand == 1 then TriggerServerEvent('ox_doorlock:breakLockpick') lib.notify({ type = 'error', description = locale('lockpick_broke') }) end StopEntityAnim(cache.ped, 'pick_door', animDict, 0) RemoveAnimDict(animDict) PickingLock = false end exports('pickClosestDoor', function() if not ClosestDoor then return end pickLock(ClosestDoor.entity) end) local tempData = {} local function addDoorlock(data) local entity = type(data) == 'number' and data or data.entity local model = GetEntityModel(entity) local coords = GetEntityCoords(entity) AddDoorToSystem(`temp`, model, coords.x, coords.y, coords.z, false, false, false) DoorSystemSetDoorState(`temp`, 4, false, false) coords = GetEntityCoords(entity) tempData[#tempData + 1] = { entity = entity, model = model, coords = coords, heading = math.floor(GetEntityHeading(entity) + 0.5) } RemoveDoorFromSystem(`temp`) end local isAddingDoorlock = false RegisterNUICallback('notify', function(data, cb) cb(1) lib.notify({ title = data }) end) RegisterNUICallback('createDoor', function(data, cb) cb(1) SetNuiFocus(false, false) data.state = data.state and 1 or 0 if data.items and not next(data.items) then data.items = nil end if data.characters and not next(data.characters) then data.characters = nil end if data.lockpickDifficulty and not next(data.lockpickDifficulty) then data.lockpickDifficulty = nil end if data.groups and not next(data.groups) then data.groups = nil end if not data.id then isAddingDoorlock = true local doorCount = data.doors and 2 or 1 local lastEntity = 0 lib.showTextUI(locale('add_door_textui')) repeat DisablePlayerFiring(cache.playerId, true) DisableControlAction(0, 25, true) local hit, entity, coords = lib.raycast.cam(1|16) local changedEntity = lastEntity ~= entity local doorA = tempData[1]?.entity if changedEntity and lastEntity ~= doorA then SetEntityDrawOutline(lastEntity, false) end lastEntity = entity if hit then ---@diagnostic disable-next-line: param-type-mismatch DrawMarker(28, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.2, 0.2, 255, 42, 24, 100, false, false, 0, true, false, false, false) end if hit and entity > 0 and GetEntityType(entity) == 3 and (doorCount == 1 or doorA ~= entity) and entityIsNotDoor(entity) then if changedEntity then SetEntityDrawOutline(entity, true) end if IsDisabledControlJustPressed(0, 24) then addDoorlock(entity) end end if IsDisabledControlJustPressed(0, 25) then SetEntityDrawOutline(entity, false) if not doorA then isAddingDoorlock = false return lib.hideTextUI() end SetEntityDrawOutline(doorA, false) table.wipe(tempData) end until tempData[doorCount] lib.hideTextUI() SetEntityDrawOutline(tempData[1].entity, false) if data.doors then SetEntityDrawOutline(tempData[2].entity, false) tempData[1].entity = nil tempData[2].entity = nil data.doors = tempData else data.model = tempData[1].model data.coords = tempData[1].coords data.heading = tempData[1].heading end else if data.doors then for i = 1, 2 do local coords = data.doors[i].coords data.doors[i].coords = vector3(coords.x, coords.y, coords.z) data.doors[i].entity = nil end else data.entity = nil end data.coords = vector3(data.coords.x, data.coords.y, data.coords.z) data.distance = nil data.zone = nil end isAddingDoorlock = false ------------------------------------ ------- Brutal Housing Editing ----- ------------------------------------ if PropertyID ~= nil then if data.name:match("^(.-)_") == nil or data.name:match("^(.-)_") ~= PropertyID then data.name = PropertyID.."_"..data.name end local coords = data.coords or data.doors[1].coords if coords ~= nil then if #(coords - vector3(PropertyCoords.x, PropertyCoords.y, PropertyCoords.z)) > MaxDoorlockDistance then TriggerEvent('brutal_housing:client:SendNotifyNumber', 75) return end else return end PropertyID = nil PropertyCoords = nil end ------------------------------------ ------- Brutal Housing Editing ----- ------------------------------------ TriggerServerEvent('ox_doorlock:editDoorlock', data.id or false, data) table.wipe(tempData) end) RegisterNUICallback('deleteDoor', function(id, cb) cb(1) TriggerServerEvent('ox_doorlock:editDoorlock', id) end) RegisterNUICallback('teleportToDoor', function(id, cb) cb(1) SetNuiFocus(false, false) local doorCoords = doors[id].coords if not doorCoords then return end SetEntityCoords(cache.ped, doorCoords.x, doorCoords.y, doorCoords.z, false, false, false, false) end) RegisterNUICallback('exit', function(_, cb) cb(1) SetNuiFocus(false, false) end) ------------------------------------ ------- Brutal Housing Editing ----- ------------------------------------ PropertyID = nil PropertyCoords = nil MaxDoorlockDistance = 50.0 RegisterNetEvent("ox_doorlock:dataFromHousing") AddEventHandler("ox_doorlock:dataFromHousing", function(propertyID, coords, maxdoorlockdistance) PropertyID = propertyID PropertyCoords = coords MaxDoorlockDistance = maxdoorlockdistance if isAddingDoorlock then return end NuiHasLoaded = true local allowedDoors = {} for k,v in pairs(doors) do print(v.name:match("^(.-)_"), PropertyID) if v.name:match("^(.-)_") == PropertyID then table.insert(allowedDoors, v) end end SendNuiMessage(json.encode({ action = 'updateDoorData', data = allowedDoors }, { with_hole = false })) Wait(100) SendNUIMessage({ action = 'setSoundFiles', data = lib.callback.await('ox_doorlock:getSounds', false) }) SetNuiFocus(true, true) SendNuiMessage(json.encode({ action = 'setVisible', data = id })) end) ------------------------------------ ------- Brutal Housing Editing ----- ------------------------------------ local function openUi(id) if source == '' or isAddingDoorlock then return end ------------------------------------ ------- Brutal Housing Editing ----- ------------------------------------ PropertyID = nil PropertyCoords = nil ------------------------------------ ------- Brutal Housing Editing ----- ------------------------------------ NuiHasLoaded = true SendNuiMessage(json.encode({ action = 'updateDoorData', data = doors }, { with_hole = false })) Wait(100) SendNUIMessage({ action = 'setSoundFiles', data = lib.callback.await('ox_doorlock:getSounds', false) }) SetNuiFocus(true, true) SendNuiMessage(json.encode({ action = 'setVisible', data = id })) end RegisterNetEvent('ox_doorlock:triggeredCommand', function(closest) openUi(closest and ClosestDoor?.id or nil) end) CreateThread(function() local target if GetResourceState('ox_target'):find('start') then target = { ox = true, exp = exports.ox_target } elseif GetResourceState('qb-target'):find('start') then target = { qb = true, exp = exports['qb-target'] } elseif GetResourceState('qtarget'):find('start') then target = { qt = true, exp = exports.qtarget } end if not target then return end if target.ox then target.exp:addGlobalObject({ { name = 'pickDoorlock', label = locale('pick_lock'), icon = 'fas fa-user-lock', onSelect = pickLock, canInteract = canPickLock, items = Config.LockpickItems, anyItem = true, distance = 1 } }) else local options = { { label = locale('pick_lock'), icon = 'fas fa-user-lock', action = pickLock, canInteract = canPickLock, item = Config.LockpickItems[1], distance = 1 } } ---@cast target table if target.qt then target.exp:Object({ options = options }) elseif target.qb then target.exp:AddGlobalObject({ options = options }) end options = { locale('pick_lock') } AddEventHandler('onResourceStop', function(resource) if resource == cache.resource then if target.qt then return target.exp:RemoveObject(options) end if target.qb then return target.exp:RemoveGlobalObject(options) end end end) end end)