mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-28 22:01:40 +00:00
309 lines
9.5 KiB
Lua
309 lines
9.5 KiB
Lua
SkyPhoneFocus = {}
|
|
|
|
local blocked_phone_controls = { 24, 140, 141, 142, 199, 200, 257, 263, 264 }
|
|
local blocked_phone_look_controls = { 1, 2, 3, 4, 5, 6 }
|
|
local focused_control_groups = { 0, 1, 2 }
|
|
local hold_to_look_enabled = false
|
|
local hold_to_look_control
|
|
local state = {
|
|
admin_panel_open = false,
|
|
activity_suspended = false,
|
|
allow_movement = false,
|
|
call_focus = false,
|
|
camera_active = false,
|
|
camera_nui_focused = true,
|
|
cursor_disabled = false,
|
|
external_game_input = nil,
|
|
external_game_input_owner = nil,
|
|
is_open = false,
|
|
look_passthrough = false,
|
|
notification_focus = false,
|
|
payphone_focus = false,
|
|
sim_picker_open = false,
|
|
text_input_focused = false,
|
|
}
|
|
local block_game = false
|
|
local block_look = false
|
|
local game_input = false
|
|
|
|
local function refresh_focus_configuration()
|
|
local hold_to_look_config = Config.Phone.HoldToLook
|
|
local enabled = type(hold_to_look_config) == "table" and hold_to_look_config.Enabled == true
|
|
local control = enabled and tonumber(hold_to_look_config.Control) or nil
|
|
if enabled and (
|
|
not control
|
|
or control ~= math.floor(control)
|
|
or control < 0
|
|
) then
|
|
error("[sky_phone] Config.Phone.HoldToLook.Control must be a non-negative whole control index.")
|
|
end
|
|
|
|
hold_to_look_enabled = enabled
|
|
hold_to_look_control = control
|
|
state.allow_movement = Config.Phone.AllowMovement == true
|
|
end
|
|
|
|
local function allows_game_input(value)
|
|
local allowed = value.external_game_input
|
|
if allowed == nil then
|
|
allowed = value.allow_movement
|
|
end
|
|
return allowed == true
|
|
end
|
|
|
|
refresh_focus_configuration()
|
|
|
|
AddEventHandler("sky_phone:configurator:updated", function()
|
|
refresh_focus_configuration()
|
|
SkyPhoneFocus.Reapply()
|
|
end)
|
|
|
|
function SkyPhoneFocus.IsHoldToLookPressed()
|
|
if not hold_to_look_enabled then
|
|
return false
|
|
end
|
|
if IsControlPressed(0, hold_to_look_control) then
|
|
return true
|
|
end
|
|
return IsDisabledControlPressed(0, hold_to_look_control)
|
|
end
|
|
|
|
function SkyPhoneFocus.ApplyFocusedControls()
|
|
for _, group in ipairs(focused_control_groups) do
|
|
DisableAllControlActions(group)
|
|
end
|
|
DisablePlayerFiring(PlayerId(), true)
|
|
end
|
|
|
|
function SkyPhoneFocus.ApplyGameInputControls(block_look)
|
|
for _, control in ipairs(blocked_phone_controls) do
|
|
DisableControlAction(0, control, true)
|
|
end
|
|
if block_look then
|
|
for _, control in ipairs(blocked_phone_look_controls) do
|
|
DisableControlAction(0, control, true)
|
|
end
|
|
end
|
|
DisablePlayerFiring(PlayerId(), true)
|
|
end
|
|
|
|
function SkyPhoneFocus.Resolve(state)
|
|
if state.admin_panel_open then
|
|
return {
|
|
block_game = true,
|
|
cursor = true,
|
|
focused = true,
|
|
game_input = false,
|
|
keep_input = false,
|
|
}
|
|
end
|
|
if state.activity_suspended then
|
|
return { block_game = false, cursor = false, focused = false, game_input = false, keep_input = false }
|
|
end
|
|
if state.call_focus then
|
|
return { block_game = true, cursor = true, focused = true, game_input = false, keep_input = false }
|
|
end
|
|
if state.camera_active and not state.camera_nui_focused then
|
|
return { block_game = false, block_look = false, cursor = false, focused = true, game_input = true, keep_input = true }
|
|
end
|
|
if state.camera_active then
|
|
-- Forward controls so disabled inputs remain readable while the NUI cursor owns focus.
|
|
return { block_game = true, block_look = true, cursor = true, focused = true, game_input = false, keep_input = true }
|
|
end
|
|
local game_input = state.is_open
|
|
and allows_game_input(state)
|
|
and not state.camera_active
|
|
and not state.text_input_focused
|
|
local focused = state.is_open
|
|
or state.notification_focus
|
|
or state.payphone_focus
|
|
or state.sim_picker_open
|
|
or (state.camera_active and state.camera_nui_focused)
|
|
local cursor = focused and not (state.is_open and (state.cursor_disabled or state.look_passthrough))
|
|
return {
|
|
block_game = cursor and not game_input,
|
|
block_look = game_input and cursor,
|
|
cursor = cursor,
|
|
focused = focused,
|
|
game_input = game_input,
|
|
keep_input = game_input,
|
|
}
|
|
end
|
|
|
|
function SkyPhoneFocus.Reapply()
|
|
if state.look_passthrough and (
|
|
not hold_to_look_enabled
|
|
or not state.is_open
|
|
or state.cursor_disabled
|
|
or state.text_input_focused
|
|
or state.camera_active
|
|
or not allows_game_input(state)
|
|
) then
|
|
state.look_passthrough = false
|
|
end
|
|
local focus = SkyPhoneFocus.Resolve(state)
|
|
SetNuiFocus(focus.focused, focus.cursor)
|
|
SetNuiFocusKeepInput(focus.keep_input)
|
|
block_game = focus.block_game == true
|
|
block_look = focus.block_look == true
|
|
game_input = focus.game_input == true
|
|
TriggerEvent("sky_phone:client:cameraFocusApplied", {
|
|
active = state.camera_active,
|
|
cursor = focus.cursor,
|
|
focused = focus.focused,
|
|
gameInput = focus.game_input,
|
|
})
|
|
end
|
|
|
|
function SkyPhoneFocus.BeginNuiHydration()
|
|
-- Browser-owned focus claims cannot survive a CEF reload.
|
|
state.notification_focus = false
|
|
state.look_passthrough = false
|
|
state.text_input_focused = false
|
|
end
|
|
|
|
function SkyPhoneFocus.SetPhone(open, cursor_disabled)
|
|
state.is_open = open == true
|
|
if cursor_disabled ~= nil then
|
|
state.cursor_disabled = cursor_disabled == true
|
|
end
|
|
if state.is_open then
|
|
state.notification_focus = false
|
|
state.call_focus = false
|
|
else
|
|
state.activity_suspended = false
|
|
state.call_focus = false
|
|
state.cursor_disabled = false
|
|
state.external_game_input = nil
|
|
state.external_game_input_owner = nil
|
|
state.look_passthrough = false
|
|
state.text_input_focused = false
|
|
end
|
|
SkyPhoneFocus.Reapply()
|
|
end
|
|
|
|
function SkyPhoneFocus.SetAdminPanel(open)
|
|
state.admin_panel_open = open == true
|
|
if state.admin_panel_open then
|
|
state.notification_focus = false
|
|
state.text_input_focused = false
|
|
end
|
|
SkyPhoneFocus.Reapply()
|
|
end
|
|
|
|
function SkyPhoneFocus.SetCall(active)
|
|
state.call_focus = active == true
|
|
SkyPhoneFocus.Reapply()
|
|
end
|
|
|
|
function SkyPhoneFocus.SetSimPicker(active)
|
|
state.sim_picker_open = active == true
|
|
SkyPhoneFocus.Reapply()
|
|
end
|
|
|
|
function SkyPhoneFocus.SetTextInputFocused(active)
|
|
state.text_input_focused = active == true
|
|
if state.text_input_focused then
|
|
state.look_passthrough = false
|
|
end
|
|
SkyPhoneFocus.Reapply()
|
|
end
|
|
|
|
function SkyPhoneFocus.SetExternalGameInput(owner_resource, allow_game_input)
|
|
if type(owner_resource) ~= "string" or owner_resource == "" or type(allow_game_input) ~= "boolean" then
|
|
return false, "invalid_focus_claim"
|
|
end
|
|
if not state.is_open then
|
|
return false, "phone_closed"
|
|
end
|
|
|
|
state.external_game_input = allow_game_input
|
|
state.external_game_input_owner = owner_resource
|
|
SkyPhoneFocus.Reapply()
|
|
return true
|
|
end
|
|
|
|
function SkyPhoneFocus.Reset()
|
|
state.admin_panel_open = false
|
|
state.activity_suspended = false
|
|
state.call_focus = false
|
|
state.camera_active = false
|
|
state.camera_nui_focused = true
|
|
state.cursor_disabled = false
|
|
state.external_game_input = nil
|
|
state.external_game_input_owner = nil
|
|
state.is_open = false
|
|
state.look_passthrough = false
|
|
state.notification_focus = false
|
|
state.payphone_focus = false
|
|
state.sim_picker_open = false
|
|
state.text_input_focused = false
|
|
block_game = false
|
|
block_look = false
|
|
game_input = false
|
|
SetNuiFocusKeepInput(false)
|
|
SetNuiFocus(false, false)
|
|
end
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
if game_input or block_game then
|
|
local look_passthrough = game_input
|
|
and not state.cursor_disabled
|
|
and SkyPhoneFocus.IsHoldToLookPressed()
|
|
if look_passthrough ~= state.look_passthrough then
|
|
state.look_passthrough = look_passthrough
|
|
SkyPhoneFocus.Reapply()
|
|
end
|
|
if block_game then
|
|
SkyPhoneFocus.ApplyFocusedControls()
|
|
else
|
|
SkyPhoneFocus.ApplyGameInputControls(block_look)
|
|
end
|
|
Wait(0)
|
|
else
|
|
Wait(250)
|
|
end
|
|
end
|
|
end)
|
|
|
|
AddEventHandler("sky_phone:client:setSuspended", function(suspended)
|
|
state.activity_suspended = suspended == true
|
|
SkyPhoneFocus.Reapply()
|
|
end)
|
|
|
|
AddEventHandler("sky_phone:client:setPayphoneFocus", function(focused)
|
|
state.payphone_focus = focused == true
|
|
SkyPhoneFocus.Reapply()
|
|
end)
|
|
|
|
AddEventHandler("sky_phone:client:setCameraFocus", function(data)
|
|
if type(data) ~= "table" or type(data.active) ~= "boolean" or type(data.nuiFocused) ~= "boolean" then
|
|
Bridge.Debug("error", "[sky_phone] Rejected invalid camera focus claim.")
|
|
return
|
|
end
|
|
state.camera_active = data.active
|
|
state.camera_nui_focused = data.nuiFocused
|
|
SkyPhoneFocus.Reapply()
|
|
end)
|
|
|
|
AddEventHandler("onClientResourceStop", function(resource_name)
|
|
if resource_name ~= state.external_game_input_owner then
|
|
return
|
|
end
|
|
|
|
state.external_game_input = nil
|
|
state.external_game_input_owner = nil
|
|
SkyPhoneFocus.Reapply()
|
|
end)
|
|
|
|
RegisterNUICallback("notification:focus", function(data, cb)
|
|
if type(data) ~= "table" or type(data.active) ~= "boolean" then
|
|
cb({ success = false, error = "invalid_request" })
|
|
return
|
|
end
|
|
state.notification_focus = data.active == true and not state.is_open
|
|
SkyPhoneFocus.Reapply()
|
|
cb({ success = true })
|
|
end)
|