Files
sky_phone-sky-systems/sky_phone/source/client/camera.lua
T
2026-08-14 10:46:36 +02:00

536 lines
17 KiB
Lua

local first_person_view_mode = 4
local third_person_view_mode = 1
local front_camera_view_mode = 0
local front_camera_fov = 25.0
local ultrawide_fov_multiplier = 2.0
local front_camera_distance = 0.75
local front_camera_height = 0.05
local front_camera_target_height = 0.03
local unfocused_camera_controls = {
1, -- INPUT_LOOK_LR
2, -- INPUT_LOOK_UD
}
local camera_state = {
active = false,
enforcing = false,
flash_enabled = false,
flash_thread = false,
focus_watcher = false,
front_camera = false,
front_camera_handle = nil,
landscape = false,
ultrawide_camera_handle = nil,
applied_nui_focus = true,
nui_focused = true,
previous_ped_view = nil,
previous_radar_hidden = nil,
previous_vehicle_view = nil,
zoom = 1.0,
}
local function rotation_to_direction(rotation)
local z = math.rad(rotation.z)
local x = math.rad(rotation.x)
local horizontal = math.abs(math.cos(x))
return vector3(-math.sin(z) * horizontal, math.cos(z) * horizontal, math.sin(x))
end
local function draw_flash_light()
local camera_coords = GetGameplayCamCoord()
local direction = rotation_to_direction(GetGameplayCamRot(2))
local light_position = camera_coords + (direction * 0.8)
DrawLightWithRange(light_position.x, light_position.y, light_position.z, 255, 255, 255, 12.0, 8.0)
end
local function set_flash_enabled(enabled)
camera_state.flash_enabled = enabled
if not enabled or camera_state.flash_thread then
return
end
camera_state.flash_thread = true
CreateThread(function()
while camera_state.flash_enabled do
draw_flash_light()
Wait(0)
end
camera_state.flash_thread = false
end)
end
local function apply_camera_view()
local ped = PlayerPedId()
local view_mode = camera_state.front_camera and front_camera_view_mode or first_person_view_mode
if IsPedInAnyVehicle(ped, false) then
SetFollowVehicleCamViewMode(view_mode)
return
end
SetFollowPedCamViewMode(view_mode)
end
local function front_camera_position(ped)
local head = GetPedBoneCoords(ped, 31086, 0.0, 0.0, 0.0)
local forward = GetEntityForwardVector(ped)
local forward_vector = vector3(forward.x, forward.y, forward.z)
local offset = forward_vector * front_camera_distance
local camera_position = head + offset + vector3(0.0, 0.0, front_camera_height)
local to_camera = camera_position - head
local dot = (to_camera.x * forward_vector.x) + (to_camera.y * forward_vector.y) + (to_camera.z * forward_vector.z)
if dot < 0.0 then
camera_position = head - offset + vector3(0.0, 0.0, front_camera_height)
end
return camera_position, head + vector3(0.0, 0.0, front_camera_target_height)
end
local function ensure_front_camera(ped)
if camera_state.front_camera_handle and DoesCamExist(camera_state.front_camera_handle) then
return
end
camera_state.front_camera_handle = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
SetCamFov(
camera_state.front_camera_handle,
camera_state.zoom == 0.5 and front_camera_fov * ultrawide_fov_multiplier or front_camera_fov
)
SetCamActive(camera_state.front_camera_handle, true)
RenderScriptCams(true, false, 0, true, true)
end
local function ensure_ultrawide_camera()
if camera_state.ultrawide_camera_handle and DoesCamExist(camera_state.ultrawide_camera_handle) then
return
end
local camera_coords = GetGameplayCamCoord()
local camera_rotation = GetGameplayCamRot(2)
camera_state.ultrawide_camera_handle = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
SetCamCoord(
camera_state.ultrawide_camera_handle,
camera_coords.x,
camera_coords.y,
camera_coords.z
)
SetCamRot(
camera_state.ultrawide_camera_handle,
camera_rotation.x,
camera_rotation.y,
camera_rotation.z,
2
)
SetCamFov(
camera_state.ultrawide_camera_handle,
math.min(GetGameplayCamFov() * ultrawide_fov_multiplier, 120.0)
)
SetCamActive(camera_state.ultrawide_camera_handle, true)
RenderScriptCams(true, false, 0, true, true)
end
local function clear_ultrawide_camera()
if camera_state.ultrawide_camera_handle and DoesCamExist(camera_state.ultrawide_camera_handle) then
RenderScriptCams(false, false, 0, true, true)
DestroyCam(camera_state.ultrawide_camera_handle, false)
end
camera_state.ultrawide_camera_handle = nil
end
local function clear_front_camera()
if camera_state.front_camera_handle and DoesCamExist(camera_state.front_camera_handle) then
RenderScriptCams(false, false, 0, true, true)
DestroyCam(camera_state.front_camera_handle, false)
end
camera_state.front_camera_handle = nil
end
local function restore_camera_view()
if camera_state.previous_ped_view ~= nil then
SetFollowPedCamViewMode(camera_state.previous_ped_view)
end
if camera_state.previous_vehicle_view ~= nil then
SetFollowVehicleCamViewMode(camera_state.previous_vehicle_view)
end
if camera_state.previous_radar_hidden ~= nil then
DisplayRadar(not camera_state.previous_radar_hidden)
end
end
local function apply_unfocused_camera_controls()
DisableAllControlActions(0)
for _, control in ipairs(unfocused_camera_controls) do
EnableControlAction(0, control, true)
end
DisablePlayerFiring(PlayerId(), true)
end
local function update_camera_focus_claim()
TriggerEvent("sky_phone:client:setCameraFocus", {
active = camera_state.active,
nuiFocused = camera_state.nui_focused,
})
end
local set_camera_focus
local function watch_unfocused_camera_controls()
if camera_state.focus_watcher then
return
end
camera_state.focus_watcher = true
CreateThread(function()
while camera_state.active and not camera_state.applied_nui_focus do
apply_unfocused_camera_controls()
if IsDisabledControlJustReleased(0, 22) then
set_camera_focus(true)
break
end
Wait(0)
end
camera_state.focus_watcher = false
end)
end
set_camera_focus = function(focused)
camera_state.nui_focused = focused
update_camera_focus_claim()
end
AddEventHandler("sky_phone:client:cameraFocusApplied", function(data)
if type(data) ~= "table"
or type(data.active) ~= "boolean"
or type(data.focused) ~= "boolean"
or type(data.gameInput) ~= "boolean"
then
return
end
if camera_state.applied_nui_focus ~= data.focused then
camera_state.applied_nui_focus = data.focused
SendNUIMessage({ type = "camera:focus", data = { focused = data.focused } })
end
if data.active and data.gameInput then
watch_unfocused_camera_controls()
end
end)
local function set_camera_active(active)
if camera_state.active == active then
return
end
camera_state.active = active
if active then
camera_state.front_camera = false
camera_state.landscape = false
camera_state.zoom = 1.0
clear_front_camera()
clear_ultrawide_camera()
camera_state.previous_ped_view = GetFollowPedCamViewMode()
camera_state.previous_vehicle_view = GetFollowVehicleCamViewMode()
camera_state.previous_radar_hidden = IsRadarHidden()
DisplayRadar(false)
set_camera_focus(true)
apply_camera_view()
TriggerEvent("sky_phone:animation:camera", {
active = true,
front = camera_state.front_camera,
landscape = camera_state.landscape,
})
if camera_state.enforcing then
return
end
camera_state.enforcing = true
CreateThread(function()
local next_apply = 0
while camera_state.active do
HideHudAndRadarThisFrame()
if camera_state.front_camera then
local ped = PlayerPedId()
ensure_front_camera(ped)
local camera_position, target = front_camera_position(ped)
SetCamCoord(
camera_state.front_camera_handle,
camera_position.x,
camera_position.y,
camera_position.z
)
PointCamAtCoord(camera_state.front_camera_handle, target.x, target.y, target.z)
elseif camera_state.zoom == 0.5 then
ensure_ultrawide_camera()
local camera_coords = GetGameplayCamCoord()
local camera_rotation = GetGameplayCamRot(2)
SetCamCoord(
camera_state.ultrawide_camera_handle,
camera_coords.x,
camera_coords.y,
camera_coords.z
)
SetCamRot(
camera_state.ultrawide_camera_handle,
camera_rotation.x,
camera_rotation.y,
camera_rotation.z,
2
)
end
local now = GetGameTimer()
if now >= next_apply then
apply_camera_view()
next_apply = now + 250
end
Wait(0)
end
camera_state.enforcing = false
end)
return
end
set_flash_enabled(false)
camera_state.front_camera = false
camera_state.landscape = false
clear_front_camera()
clear_ultrawide_camera()
restore_camera_view()
camera_state.nui_focused = true
update_camera_focus_claim()
TriggerEvent("sky_phone:animation:camera", {
active = false,
front = false,
landscape = false,
})
end
local function set_front_camera(active)
if camera_state.front_camera == active then
return
end
camera_state.front_camera = active
if not camera_state.active then
return
end
if active then
clear_ultrawide_camera()
ensure_front_camera(PlayerPedId())
else
clear_front_camera()
if camera_state.zoom == 0.5 then
ensure_ultrawide_camera()
end
end
apply_camera_view()
TriggerEvent("sky_phone:animation:camera", {
active = true,
front = camera_state.front_camera,
landscape = camera_state.landscape,
})
end
local function set_camera_landscape(active)
if camera_state.landscape == active then
return
end
camera_state.landscape = active
if camera_state.active then
TriggerEvent("sky_phone:animation:camera", {
active = true,
front = camera_state.front_camera,
landscape = camera_state.landscape,
})
end
end
local function set_camera_zoom(zoom)
if zoom ~= 0.5 and zoom ~= 1.0 and zoom ~= 2.0 and zoom ~= 3.0 then
return false
end
camera_state.zoom = zoom
if not camera_state.active then
return true
end
if camera_state.front_camera then
ensure_front_camera(PlayerPedId())
SetCamFov(
camera_state.front_camera_handle,
zoom == 0.5 and front_camera_fov * ultrawide_fov_multiplier or front_camera_fov
)
elseif zoom == 0.5 then
ensure_ultrawide_camera()
else
clear_ultrawide_camera()
end
return true
end
RegisterNUICallback("camera:setActive", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
set_camera_active(data.active == true)
cb({ success = true })
end)
RegisterNUICallback("camera:setFocus", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
if camera_state.active then
set_camera_focus(data.focused == true)
end
cb({ success = true })
end)
RegisterNUICallback("camera:setFlash", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
set_flash_enabled(data.enabled == true)
cb({ success = true })
end)
RegisterNUICallback("camera:setFacing", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
set_front_camera(data.front == true)
cb({ success = true })
end)
RegisterNUICallback("camera:setOrientation", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
set_camera_landscape(data.landscape == true)
cb({ success = true })
end)
RegisterNUICallback("camera:setZoom", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
cb({ success = set_camera_zoom(tonumber(data.zoom)) })
end)
RegisterNUICallback("media:requestUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:media:request-upload", data)
cb({ success = true })
end)
RegisterNUICallback("media:completeUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:media:complete-upload", data)
cb({ success = true })
end)
RegisterNUICallback("media:cancelUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:media:cancel-upload", data)
cb({ success = true })
end)
RegisterNUICallback("media:failUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:media:fail-upload", data)
cb({ success = true })
end)
RegisterNUICallback("gallery:delete", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:media:delete", data)
cb({ success = true })
end)
RegisterNUICallback("gallery:delete-many", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:media:delete-many", data)
cb({ success = true })
end)
RegisterNUICallback("memos:requestUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:memos:request-upload", data)
cb({ success = true })
end)
RegisterNUICallback("memos:completeUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:memos:complete-upload", data)
cb({ success = true })
end)
RegisterNUICallback("memos:cancelUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:memos:cancel-upload", data)
cb({ success = true })
end)
RegisterNUICallback("memos:failUpload", function(data, cb)
if type(data) ~= "table" then
cb({ success = false, error = "invalid_request" })
return
end
TriggerServerEvent("sky_phone:memos:fail-upload", data)
cb({ success = true })
end)
RegisterNetEvent("sky_phone:media:upload-ready", function(data)
SendNUIMessage({ type = "media:uploadReady", data = data })
end)
RegisterNetEvent("sky_phone:media:upload-result", function(data)
SendNUIMessage({ type = "media:uploadResult", data = data })
end)
RegisterNetEvent("sky_phone:media:delete-result", function(data)
SendNUIMessage({ type = "media:deleteResult", data = data })
end)
RegisterNetEvent("sky_phone:media:delete-many-result", function(data)
SendNUIMessage({ type = "media:deleteManyResult", data = data })
end)
RegisterNetEvent("sky_phone:memos:upload-ready", function(data)
SendNUIMessage({ type = "memos:uploadReady", data = data })
end)
RegisterNetEvent("sky_phone:memos:upload-result", function(data)
SendNUIMessage({ type = "memos:uploadResult", data = data })
end)
AddEventHandler("sky_phone:nuiClosed", function()
set_flash_enabled(false)
set_camera_active(false)
end)
AddEventHandler("onResourceStop", function(resource_name)
if resource_name == GetCurrentResourceName() then
set_flash_enabled(false)
set_camera_active(false)
end
end)