mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 07:01:39 +00:00
493375bbfa
Keep the rear camera in first person, correct the selfie camera offset, and remove the scripted ultrawide camera that changed the whole game view. Add continuous validated zoom and an explicit look-control lock without blocking player movement.
499 lines
15 KiB
Lua
499 lines
15 KiB
Lua
local minimum_zoom = 0.5
|
|
local maximum_zoom = 3.0
|
|
local first_person_view_mode = 4
|
|
local front_camera_fov = 40.0
|
|
local front_camera_distance = 1.05
|
|
local front_camera_side_offset = 0.08
|
|
local front_camera_height = 0.12
|
|
local front_camera_target_height = -0.1
|
|
local blocked_camera_controls = {
|
|
0, -- INPUT_NEXT_CAMERA
|
|
22, -- INPUT_JUMP
|
|
24, -- INPUT_ATTACK
|
|
25, -- INPUT_AIM
|
|
37, -- INPUT_SELECT_WEAPON
|
|
44, -- INPUT_COVER
|
|
45, -- INPUT_RELOAD
|
|
68, -- INPUT_VEH_AIM
|
|
69, -- INPUT_VEH_ATTACK
|
|
70, -- INPUT_VEH_ATTACK2
|
|
91, -- INPUT_VEH_PASSENGER_AIM
|
|
92, -- INPUT_VEH_PASSENGER_ATTACK
|
|
140, -- INPUT_MELEE_ATTACK_LIGHT
|
|
141, -- INPUT_MELEE_ATTACK_HEAVY
|
|
142, -- INPUT_MELEE_ATTACK_ALTERNATE
|
|
257, -- INPUT_ATTACK2
|
|
263, -- INPUT_MELEE_ATTACK1
|
|
264, -- INPUT_MELEE_ATTACK2
|
|
}
|
|
local camera_look_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,
|
|
locked = false,
|
|
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 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 right_vector = vector3(forward_vector.y, -forward_vector.x, 0.0)
|
|
local camera_position = head
|
|
+ (forward_vector * front_camera_distance)
|
|
+ (right_vector * front_camera_side_offset)
|
|
+ vector3(0.0, 0.0, front_camera_height)
|
|
local target = head
|
|
+ (right_vector * (front_camera_side_offset * 0.25))
|
|
+ vector3(0.0, 0.0, front_camera_target_height)
|
|
return camera_position, target
|
|
end
|
|
|
|
local function ensure_front_camera()
|
|
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, front_camera_fov)
|
|
SetCamActive(camera_state.front_camera_handle, true)
|
|
RenderScriptCams(true, false, 0, true, true)
|
|
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 apply_rear_camera_view()
|
|
if camera_state.front_camera then
|
|
return
|
|
end
|
|
local ped = PlayerPedId()
|
|
if IsPedInAnyVehicle(ped, false) then
|
|
SetFollowVehicleCamViewMode(first_person_view_mode)
|
|
return
|
|
end
|
|
SetFollowPedCamViewMode(first_person_view_mode)
|
|
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_camera_controls()
|
|
for _, control in ipairs(blocked_camera_controls) do
|
|
DisableControlAction(0, control, true)
|
|
end
|
|
if camera_state.locked then
|
|
for _, control in ipairs(camera_look_controls) do
|
|
DisableControlAction(0, control, true)
|
|
end
|
|
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_camera_controls()
|
|
if camera_state.focus_watcher then
|
|
return
|
|
end
|
|
camera_state.focus_watcher = true
|
|
CreateThread(function()
|
|
while camera_state.active do
|
|
apply_camera_controls()
|
|
if not camera_state.applied_nui_focus and IsDisabledControlJustReleased(0, 22) then
|
|
set_camera_focus(true)
|
|
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_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.locked = false
|
|
camera_state.zoom = 1.0
|
|
clear_front_camera()
|
|
camera_state.previous_ped_view = GetFollowPedCamViewMode()
|
|
camera_state.previous_radar_hidden = IsRadarHidden()
|
|
camera_state.previous_vehicle_view = GetFollowVehicleCamViewMode()
|
|
DisplayRadar(false)
|
|
set_camera_focus(true)
|
|
apply_rear_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_view_apply = 0
|
|
while camera_state.active do
|
|
HideHudAndRadarThisFrame()
|
|
if camera_state.front_camera then
|
|
local ped = PlayerPedId()
|
|
ensure_front_camera()
|
|
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)
|
|
else
|
|
local now = GetGameTimer()
|
|
if now >= next_view_apply then
|
|
apply_rear_camera_view()
|
|
next_view_apply = now + 250
|
|
end
|
|
end
|
|
Wait(0)
|
|
end
|
|
camera_state.enforcing = false
|
|
end)
|
|
return
|
|
end
|
|
set_flash_enabled(false)
|
|
camera_state.front_camera = false
|
|
camera_state.landscape = false
|
|
camera_state.locked = false
|
|
clear_front_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
|
|
ensure_front_camera()
|
|
else
|
|
clear_front_camera()
|
|
apply_rear_camera_view()
|
|
end
|
|
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 not zoom or zoom < minimum_zoom or zoom > maximum_zoom then
|
|
return false
|
|
end
|
|
zoom = math.floor((zoom * 100.0) + 0.5) / 100.0
|
|
camera_state.zoom = zoom
|
|
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:setLocked", function(data, cb)
|
|
if type(data) ~= "table" then
|
|
cb({ success = false, error = "invalid_request" })
|
|
return
|
|
end
|
|
camera_state.locked = data.locked == true
|
|
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)
|