Files
sky_phone/sky_phone/source/client/housing.lua
T
2026-08-19 20:11:36 +02:00

267 lines
9.0 KiB
Lua

local camera_state = nil
local function server_prepare(action, data)
local request = {
action = action,
propertyId = data and data.propertyId,
target = data and data.target,
identifier = data and data.identifier,
}
return Bridge.Callbacks.Trigger("sky_phone:housing:prepare", request)
end
local function suspend_phone()
TriggerEvent("sky_phone:client:setSuspended", true)
TriggerEvent("sky_phone:animation:phone", false)
SendNUIMessage({ type = "app:suspend" })
end
local function resume_phone()
SendNUIMessage({ type = "app:resume" })
TriggerEvent("sky_phone:client:setSuspended", false)
TriggerEvent("sky_phone:animation:phone", true)
end
local function stop_camera(resume)
local state = camera_state
if not state then
return
end
camera_state = nil
RenderScriptCams(false, true, 350, true, true)
if state.camera and DoesCamExist(state.camera) then
DestroyCam(state.camera, false)
end
ClearFocus()
SetNightvision(state.night_vision)
if DoesEntityExist(state.ped) then
FreezeEntityPosition(state.ped, state.frozen)
end
if resume ~= false then
resume_phone()
end
end
local function camera_number(value, fallback)
local number = tonumber(value)
if not number or number ~= number or number == math.huge or number == -math.huge then
return fallback
end
return number
end
local function run_camera(provider_name, camera_data)
if camera_state or type(camera_data) ~= "table"
or type(camera_data.coords) ~= "table"
or type(camera_data.rotation) ~= "table"
then
return
end
local coords = {
x = camera_number(camera_data.coords.x),
y = camera_number(camera_data.coords.y),
z = camera_number(camera_data.coords.z),
}
local rotation = {
x = camera_number(camera_data.rotation.x),
y = camera_number(camera_data.rotation.y, 0.0),
z = camera_number(camera_data.rotation.z),
}
if not coords.x or not coords.y or not coords.z or not rotation.x or not rotation.z then
Bridge.Debug("error", "[sky_phone] Rejected invalid housing camera data.")
return
end
local config = Config.Housing.Camera
local ped = PlayerPedId()
local camera = CreateCamWithParams(
"DEFAULT_SCRIPTED_CAMERA",
coords.x,
coords.y,
coords.z,
rotation.x,
rotation.y,
rotation.z,
config.FieldOfView,
true,
2
)
if not camera or camera == 0 then
Bridge.Debug("error", "[sky_phone] Failed to create housing camera.")
return
end
camera_state = {
camera = camera,
frozen = IsEntityPositionFrozen(ped),
night_vision = GetUsingnightvision(),
ped = ped,
provider = provider_name,
}
suspend_phone()
FreezeEntityPosition(ped, true)
SetFocusArea(coords.x, coords.y, coords.z, 0.0, 0.0, 0.0)
SetCamActive(camera, true)
RenderScriptCams(true, true, 350, true, true)
local maximum_left = camera_number(camera_data.maximumLeft)
local maximum_right = camera_number(camera_data.maximumRight)
local night_vision = camera_state.night_vision
local resume_after_camera = false
local close_phone_after_camera = false
while camera_state and camera_state.camera == camera do
Wait(0)
DisableAllControlActions(0)
if IsDisabledControlJustPressed(0, config.ExitControl) then
resume_after_camera = true
break
end
if IsPedDeadOrDying(ped, true) or GetResourceState(provider_name) ~= "started" then
close_phone_after_camera = true
break
end
local camera_rotation = GetCamRot(camera, 2)
local next_x = camera_rotation.x
local next_z = camera_rotation.z
if IsDisabledControlPressed(0, config.LeftControl)
and (not maximum_left or next_z < maximum_left)
then
next_z = next_z + config.RotateSpeed
elseif IsDisabledControlPressed(0, config.RightControl)
and (not maximum_right or next_z > maximum_right)
then
next_z = next_z - config.RotateSpeed
end
if IsDisabledControlPressed(0, config.UpControl) then
next_x = math.min(85.0, next_x + config.RotateSpeed)
elseif IsDisabledControlPressed(0, config.DownControl) then
next_x = math.max(-85.0, next_x - config.RotateSpeed)
end
if next_x ~= camera_rotation.x or next_z ~= camera_rotation.z then
SetCamRot(camera, next_x, camera_rotation.y, next_z, 2)
end
local field_of_view = GetCamFov(camera)
if IsDisabledControlJustPressed(0, config.ZoomInControl) then
SetCamFov(camera, math.max(config.MinimumFieldOfView, field_of_view - 3.0))
elseif IsDisabledControlJustPressed(0, config.ZoomOutControl) then
SetCamFov(camera, math.min(config.MaximumFieldOfView, field_of_view + 3.0))
end
if IsDisabledControlJustPressed(0, config.NightVisionControl) then
night_vision = not night_vision
SetNightvision(night_vision)
end
end
stop_camera(resume_after_camera)
if close_phone_after_camera then
TriggerEvent("sky_phone:client:forceClose")
end
end
RegisterNetEvent("sky_phone:device:invalidated", function()
stop_camera(false)
end)
RegisterNUICallback("housing:overview", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
local result = Bridge.Callbacks.Trigger("sky_phone:housing:overview", {})
if type(result) == "table" and result.success and type(result.data) == "table" then
local properties, error_code = Bridge.Housing.EnrichOverview(
result.data.provider,
result.data.properties
)
if not properties then
cb({ success = false, error = error_code or "provider_error" })
return
end
result.data.properties = properties
end
cb(type(result) == "table" and result or { success = false, error = "request_failed" })
end)
AddEventHandler("sky_phone:client:nuiReady", function()
if camera_state then
suspend_phone()
end
end)
RegisterNUICallback("housing:key-candidates", function(data, cb)
if type(data) ~= "table" or type(data.propertyId) ~= "string" then
cb({ success = false, error = "invalid_request" })
return
end
local result = server_prepare("key_candidates", data)
cb(type(result) == "table" and result or { success = false, error = "request_failed" })
end)
RegisterNUICallback("housing:command", function(data, cb)
if type(data) ~= "table" or type(data.action) ~= "string" or type(data.propertyId) ~= "string" then
cb({ success = false, error = "invalid_request" })
return
end
local actions = {
grant_key = true,
open_cctv = true,
revoke_key = true,
set_waypoint = true,
toggle_lock = true,
}
if not actions[data.action] then
cb({ success = false, error = "invalid_action" })
return
end
local prepared = server_prepare(data.action, data)
if type(prepared) ~= "table" or not prepared.success or type(prepared.data) ~= "table" then
cb(type(prepared) == "table" and prepared or { success = false, error = "request_failed" })
return
end
if data.action == "set_waypoint" then
local coords = prepared.data.coords
if coords == nil then
local success, error_code = Bridge.Housing.Execute(
prepared.data.provider,
data.action,
prepared.data
)
cb(success and { success = true }
or { success = false, error = error_code or "invalid_coordinates" })
return
end
local x = type(coords) == "table" and camera_number(coords.x) or nil
local y = type(coords) == "table" and camera_number(coords.y) or nil
if not x or not y then
cb({ success = false, error = "invalid_coordinates" })
return
end
SetNewWaypoint(x + 0.0, y + 0.0)
cb({ success = true })
return
end
if data.action == "open_cctv" then
if type(prepared.data.camera) ~= "table" or type(prepared.data.provider) ~= "string" then
cb({ success = false, error = "cctv_unavailable" })
return
end
cb({ success = true })
CreateThread(function()
run_camera(prepared.data.provider, prepared.data.camera)
end)
return
end
local success, error_code = Bridge.Housing.Execute(prepared.data.provider, data.action, prepared.data)
cb(success and { success = true } or { success = false, error = error_code or "provider_rejected" })
end)
AddEventHandler("onResourceStop", function(resource_name)
if resource_name == GetCurrentResourceName() and camera_state then
stop_camera(false)
end
end)