1
0
Fork 0
forked from Simnation/Main
Main/resources/[housing]/qs-housing/client/custom/target/qb-target.lua

462 lines
18 KiB
Lua
Raw Normal View History

2025-06-09 23:54:46 +02:00
if not Config.UseTarget then
return
end
local target_name = GetResourceState('ox_target'):find('started') and 'qtarget' or 'qb-target'
---@class Target
---@field houses table
Target = {
zones = {},
}
local function checkKey()
if CurrentHouse ~= nil and CurrentHouseData.haskey then
return true
end
return false
end
local lastMLODoors = {}
function Target:initMLODoors(key)
local houseData = self.houses[key]
if not houseData then return end
if not houseData.mlo then return end
local hashes = {}
for doorId, data in pairs(houseData.mlo) do
local has = table.find(hashes, function(v)
return v == data.hash
end)
if not has then
table.insert(hashes, data.hash)
end
end
lastMLODoors = hashes
local confirmed = {}
exports[target_name]:AddTargetModel(hashes, {
options = {
{
icon = 'fa-solid fa-door-open',
label = Lang('HOUSING_TARGET_TOGGLE_DOOR'),
action = function(entity)
local coords = GetEntityCoords(entity)
local finded, doorId = table.find(houseData.mlo, function(door)
local doorCoords = vec3(door.coords.x, door.coords.y, door.coords.z)
local distance = #(coords - doorCoords)
return distance < Config.DoorDistance
end)
if not finded then return end
local doorData = houseData.mlo[doorId]
if not checkKey() then return Notification(Lang('HOUSING_NOTIFICATION_NO_KEYS'), 'error') end
RequestAnimDict('anim@heists@keycard@')
while not HasAnimDictLoaded('anim@heists@keycard@') do
Citizen.Wait(1)
end
TaskPlayAnim(PlayerPedId(), 'anim@heists@keycard@', 'exit', 8.0, 8.0, 1000, 1, 1, 0, 0, 0)
TriggerServerEvent('qb-houses:SyncDoor', CurrentHouse, { finded }, not doorData.locked)
end,
canInteract = function(entity)
if not CurrentHouse then
return false
end
if confirmed[entity] then
return true
end
local coords = GetEntityCoords(entity)
local finded = table.find(houseData.mlo, function(door)
local doorCoords = vec3(door.coords.x, door.coords.y, door.coords.z)
local distance = #(coords - doorCoords)
return distance < Config.DoorDistance
end)
if not finded then
return false
end
confirmed[entity] = finded
return true
end
},
},
distance = 2.5
})
end
function Target:initObjectInteractions()
local hashes = {}
for a, x in pairs(Config.DynamicFurnitures) do
table.insert(hashes, GetHashKey(a))
end
exports[target_name]:AddTargetModel(hashes, {
options = {
{
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_TARGET_FURNITURE_INTERACTION'),
action = function(entity) -- This is the action it has to perform, this REPLACES the event and this is OPTIONAL
local decorations = ObjectList
if not decorations then return end
local decorationData = table.find(decorations, function(decoration)
return GetHashKey(decoration.modelName) == GetEntityModel(entity) and decoration.handle == entity
end)
local objectData = table.find(Config.DynamicFurnitures, function(furniData, key)
return GetHashKey(key) == GetEntityModel(entity)
end)
if not objectData then return print('No objectData') end
if not decorationData then return print('No decorationData') end
if objectData.event then
local uniq = decorationData.uniq
TriggerEvent(objectData.event, uniq)
return
end
if objectData.type == 'stash' then
local uniq = decorationData.uniq
if CanAccessStash(uniq) then
openStash(objectData.stash, uniq)
end
elseif objectData.type == 'gardrobe' then
openWardrobe()
end
end,
canInteract = function(entity, distance, data) -- This will check if you can interact with it, this won't show up if it returns false, this is OPTIONAL
local house = CurrentHouse
if not house then
return false
end
return true
end,
},
{
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_MENU_VAULT_SET_CODE'),
action = function(entity) -- This is the action it has to perform, this REPLACES the event and this is OPTIONAL
local house = CurrentHouse
local decorations = ObjectList
if not decorations then
Notification(Lang('HOUSING_NOTIFICATION_VAULT_CANNOT_DECORATRIONS'), 'error')
return
end
local decorationData = table.find(decorations, function(decoration)
return GetHashKey(decoration.modelName) == GetEntityModel(entity)
end)
local objectData = table.find(Config.DynamicFurnitures, function(furniData, key)
return GetHashKey(key) == GetEntityModel(entity)
end)
if not objectData then
Notification(Lang('HOUSING_NOTIFICATION_VAULT_CANNOT_OBJECT_DATA'), 'error')
return
end
if not decorationData then
Notification(Lang('HOUSING_NOTIFICATION_VAULT_CANOT_DECORATION_DATA'), 'error')
return
end
if objectData.type == 'stash' then
local uniq = decorationData.uniq
OpenVaultCodeMenu(uniq)
return
end
Notification(Lang('HOUSING_NOTIFICATION_VAULT_CANNOT_SET_CODE'), 'error')
end,
canInteract = function(entity, distance, data) -- This will check if you can interact with it, this won't show up if it returns false, this is OPTIONAL
if not CurrentHouseData.isOfficialOwner then return false end
local houseData = Config.Houses[CurrentHouse]
return table.includes(houseData.upgrades, 'vault')
end,
}
},
distance = Config.TargetLength,
})
Target.initObjectInteractions = nil
end
local function checkHouseHasOwner()
if not CurrentHouseData.isOwned or CurrentHouseData.rentable or CurrentHouseData.purchasable then return false end
return true
end
function Target:initOutside(key)
local houseData = self.houses[key]
local enterCoords = vec3(houseData.coords.enter.x, houseData.coords.enter.y, houseData.coords.enter.z)
local options = {}
if houseData.apartmentNumber then
table.insert(options, {
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_TARGET_SHOW_APARTMENTS'),
action = function()
OpenApartmentMenu()
end,
canInteract = function(entity, distance, data)
if checkHouseHasOwner() then return false end
return true
end,
})
elseif not houseData.apartmentNumber then
options = {
{
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_TARGET_SHOW_HOUSE'),
action = function()
InspectHouse(houseData)
end,
canInteract = function(entity, distance, data)
if checkHouseHasOwner() then return false end
return true
end,
},
{
icon = 'fas fa-file-contract',
label = Lang('HOUSING_TARGET_VIEW_HOUSE'),
action = function()
if CurrentHouseData.rentable then
TriggerServerEvent('qb-houses:server:viewHouse', CurrentHouse, true)
else
TriggerServerEvent('qb-houses:server:viewHouse', CurrentHouse)
end
end,
canInteract = function(entity, distance, data)
if checkHouseHasOwner() then return false end
return true
end,
},
{
icon = 'fa-solid fa-door-open',
label = Lang('HOUSING_TARGET_ENTER_HOUSE'),
action = function()
TriggerEvent('qb-houses:client:EnterHouse', houseData.ipl)
end,
canInteract = function(entity, distance, data)
if not CurrentHouse then return false end
if Config.Houses[CurrentHouse].mlo then return false end
if not checkHouseHasOwner() then return false end
if not CurrentHouseData.haskey and not Config.Houses[CurrentHouse].IsRammed then return false end
return true
end,
},
{
icon = 'fa-solid fa-bell',
label = Lang('HOUSING_TARGET_REQUEST_RING'),
action = function()
TriggerEvent('qb-houses:client:RequestRing')
end,
canInteract = function(entity, distance, data)
if not CurrentHouse then return false end
if Config.Houses[CurrentHouse].mlo then return false end
if not checkHouseHasOwner() then return false end
if CurrentHouseData.haskey or Config.Houses[CurrentHouse].IsRammed then return false end
return true
end,
},
}
end
if #options == 0 then return end
exports[target_name]:AddBoxZone('house_outside' .. key, enterCoords, Config.TargetLength, Config.TargetWidth, {
name = 'house_outside' .. key,
heading = 90.0,
debugPoly = Config.ZoneDebug,
minZ = enterCoords.z - 15.0,
maxZ = enterCoords.z + 5.0,
}, {
options = options,
distance = 2.5
})
table.insert(self.zones, 'house_outside' .. key)
end
function Target:initExit(key)
local houseData = self.houses[key]
local exitCoords
if houseData.mlo then return end
if houseData.ipl then
exitCoords = vec3(houseData.ipl.exit.x, houseData.ipl.exit.y, houseData.ipl.exit.z)
else
if not houseData.coords.exit then return end
exitCoords = vec3(houseData.coords.exit.x, houseData.coords.exit.y, houseData.coords.exit.z)
end
exports[target_name]:AddBoxZone('house_exit' .. key, exitCoords, Config.TargetLength, Config.TargetWidth, {
name = 'house_exit' .. key,
heading = 90.0,
debugPoly = Config.ZoneDebug,
minZ = exitCoords.z - 15.0,
maxZ = exitCoords.z + 5.0,
}, {
options = {
{
icon = 'fa-solid fa-door-open',
label = Lang('HOUSING_TARGET_EXIT_HOUSE'),
action = function()
if houseData.ipl then
LeaveIplHouse(EnteredHouse, inOwned)
else
LeaveHouse()
end
end,
canInteract = function(entity, distance, data)
return true
end,
},
{
icon = 'fa-solid fa-bell',
label = Lang('HOUSING_TARGET_RING_DOORBELL'),
action = function()
TriggerServerEvent('qb-houses:server:OpenDoor', CurrentDoorBell, CurrentHouse)
CurrentDoorBell = 0
end,
canInteract = function(entity, distance, data)
return CurrentDoorBell ~= 0
end,
},
{
icon = 'fa-solid fa-video',
label = Lang('HOUSING_TARGET_ACCESS_CAMERA'),
action = function()
FrontDoorCam(houseData.coords.enter)
end,
canInteract = function(entity, distance, data)
if houseData.ipl then return false end
return not inOwned
end,
},
},
distance = 2.5
})
table.insert(self.zones, 'house_exit' .. key)
end
function Target:initWardrobe()
local wardrobe = CurrentHouseData.wardrobe
if not wardrobe then return Debug('Target:initWardrobe ::: No wardrobe coords') end
exports[target_name]:AddBoxZone('house_wardrobe', wardrobe, Config.TargetLength, Config.TargetWidth, {
name = 'house_wardrobe',
heading = 90.0,
debugPoly = Config.ZoneDebug,
minZ = wardrobe.z - 15.0,
maxZ = wardrobe.z + 5.0,
}, {
options = {
{
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_TARGET_WARDROBE_INTERACTION'),
action = function()
openWardrobe()
end,
canInteract = function(entity, distance, data)
return true
end,
},
},
distance = 2.5
})
end
function Target:initStash()
local stash = CurrentHouseData.stash
if not stash then return Debug('Target:initStash ::: No stash coords') end
exports[target_name]:AddBoxZone('house_stash', stash, Config.TargetLength, Config.TargetWidth, {
name = 'house_stash',
heading = 90.0,
debugPoly = Config.ZoneDebug,
minZ = stash.z - 15.0,
maxZ = stash.z + 5.0,
}, {
options = {
{
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_TARGET_STASH_INTERACTION'),
action = function()
if CanAccessStash() then
openStash()
end
end,
canInteract = function(entity, distance, data)
return true
end,
},
{
icon = 'fa-solid fa-key',
label = Lang('HOUSING_MENU_VAULT_SET_CODE'),
action = function()
OpenVaultCodeMenu()
end,
canInteract = function(entity, distance, data)
if not CurrentHouseData.isOfficialOwner then return false end
local houseData = Config.Houses[CurrentHouse]
return table.includes(houseData.upgrades, 'vault')
end,
}
},
distance = 2.5
})
end
function Target:initLogout()
local logout = CurrentHouseData.logout
if not logout then return Debug('Target:initLogout ::: No logout coords') end
exports[target_name]:AddBoxZone('house_logout', logout, Config.TargetLength, Config.TargetWidth, {
name = 'house_logout',
heading = 90.0,
debugPoly = Config.ZoneDebug,
minZ = logout.z - 15.0,
maxZ = logout.z + 5.0,
}, {
options = {
{
icon = 'fa-solid fa-magnifying-glass',
label = Lang('HOUSING_TARGET_LOGOUT_INTERACTION'),
action = function()
DoScreenFadeOut(250)
while not IsScreenFadedOut() do Wait(10) end
DespawnInterior(HouseObj, function()
WeatherSyncEvent(false) -- Weather Events
local house = CurrentHouse
SetEntityCoords(PlayerPed, Config.Houses[house].coords.enter.x, Config.Houses[house].coords.enter.y, Config.Houses[house].coords.enter.z + 0.5)
SetEntityHeading(PlayerPed, Config.Houses[house].coords.enter.h)
inOwned = false
TriggerServerEvent('qb-houses:server:LogoutLocation')
end)
end,
canInteract = function(entity, distance, data)
return true
end,
},
},
distance = 2.5
})
end
function Target:init()
for k, v in pairs(self.zones) do
exports[target_name]:RemoveZone(v)
end
self.zones = {}
exports[target_name]:RemoveTargetModel(lastMLODoors)
for k, v in pairs(self.houses) do
self:initOutside(k)
self:initMLODoors(k)
self:initExit(k)
end
end
function Target:initInsideInteractions()
exports[target_name]:RemoveZone('house_wardrobe')
exports[target_name]:RemoveZone('house_stash')
exports[target_name]:RemoveZone('house_logout')
Target:initWardrobe()
Target:initStash()
Target:initLogout()
end
function Target:formatHouses()
self.houses = table.filter(self.houses, function(house)
return not house.apartmentNumber or house.apartmentNumber == 'apt-0'
end)
end
RegisterNetEvent('housing:initHouses', function(houseConfig)
Target.houses = houseConfig
Target:formatHouses()
Target:init()
if Target.initObjectInteractions then
Target:initObjectInteractions()
end
end)