mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 00:01:29 +00:00
FIX - improve payphone call flow
This commit is contained in:
@@ -648,8 +648,8 @@ onBeforeUnmount(() => {
|
||||
width: 50.9%;
|
||||
height: 5.9%;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr);
|
||||
gap: 3%;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 3.2%;
|
||||
}
|
||||
|
||||
.payphone-action {
|
||||
@@ -676,10 +676,12 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.payphone-action--call {
|
||||
grid-column: span 2;
|
||||
background: linear-gradient(#3dbd74, #187c43);
|
||||
}
|
||||
|
||||
.payphone-action--hangup {
|
||||
grid-column: span 2;
|
||||
background: linear-gradient(#e95c55, #a12420);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@ Locales["en"] = {
|
||||
default = "The phone could not be opened.",
|
||||
},
|
||||
Payphone = {
|
||||
Interact = "Press ~INPUT_CONTEXT~ to use the payphone.",
|
||||
Interact = "Use the payphone.",
|
||||
RingingHelp = "Calling {number}... | Hang up",
|
||||
ConnectedHelp = "Connected to {number} | {duration} | {currency}{cost} | Hang up",
|
||||
},
|
||||
Nui = {
|
||||
Payphone = {
|
||||
|
||||
@@ -50,3 +50,10 @@ function Bridge.Framework.Notify(title, message, notification_type, duration)
|
||||
|
||||
Bridge.Debug("error", "[sky_phone] Notification requested for unsupported framework '%s'.", tostring(framework_name))
|
||||
end
|
||||
|
||||
function Bridge.Framework.ShowHelpNotification(message, key)
|
||||
local control = key == "E" and "~INPUT_CONTEXT~" or ("[%s]"):format(tostring(key or "E"))
|
||||
BeginTextCommandDisplayHelp("STRING")
|
||||
AddTextComponentSubstringPlayerName(("%s %s"):format(control, tostring(message or "")))
|
||||
EndTextCommandDisplayHelp(0, false, true, -1)
|
||||
end
|
||||
|
||||
@@ -2,11 +2,16 @@ local payphone_open = false
|
||||
local nearest_payphone = nil
|
||||
local active_booth = nil
|
||||
local active_call_id = nil
|
||||
local active_call_state = nil
|
||||
local active_call_number = nil
|
||||
local active_call_elapsed_seconds = 0
|
||||
local active_call_elapsed_updated_at = 0
|
||||
local call_channel = 0
|
||||
local replacement_prop = nil
|
||||
local hidden_prop = nil
|
||||
local animation_scene = nil
|
||||
local distance_hangup_requested = false
|
||||
local visuals_starting = false
|
||||
local hangup_requested = false
|
||||
|
||||
local configured_models = {}
|
||||
for _, model_name in ipairs(Config.Payphones.Props or {}) do
|
||||
@@ -64,16 +69,24 @@ end
|
||||
local function stop_call_visuals()
|
||||
local animation = Config.Payphones.Animation
|
||||
local ped = PlayerPedId()
|
||||
local visuals_active = animation_scene ~= nil or replacement_prop ~= nil
|
||||
visuals_starting = false
|
||||
|
||||
if visuals_active and DoesEntityExist(ped) then
|
||||
StopAnimTask(ped, animation.Dictionary, animation.PedClip, 0.0)
|
||||
ClearPedTasksImmediately(ped)
|
||||
end
|
||||
|
||||
if animation_scene then
|
||||
ClearPedTasks(ped)
|
||||
SetSynchronizedSceneHoldLastFrame(animation_scene, false)
|
||||
DisposeSynchronizedScene(animation_scene)
|
||||
animation_scene = nil
|
||||
else
|
||||
StopAnimTask(ped, animation.Dictionary, animation.PedClip, 2.0)
|
||||
end
|
||||
|
||||
if replacement_prop and DoesEntityExist(replacement_prop) then
|
||||
StopEntityAnim(replacement_prop, animation.PropClip, animation.Dictionary, -2.0)
|
||||
StopEntityAnim(replacement_prop, animation.PropClip, animation.Dictionary, 0.0)
|
||||
SetEntityVisible(replacement_prop, false, false)
|
||||
SetEntityAsMissionEntity(replacement_prop, true, true)
|
||||
DeleteEntity(replacement_prop)
|
||||
end
|
||||
replacement_prop = nil
|
||||
@@ -86,19 +99,32 @@ local function stop_call_visuals()
|
||||
end
|
||||
|
||||
local function start_call_visuals()
|
||||
if replacement_prop or not active_booth or not DoesEntityExist(active_booth.entity) then
|
||||
if visuals_starting or replacement_prop or not active_call_id
|
||||
or not active_booth or not DoesEntityExist(active_booth.entity)
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
visuals_starting = true
|
||||
local expected_call_id = active_call_id
|
||||
local booth = active_booth
|
||||
local replacement_hash = joaat(Config.Payphones.ReplacementProp)
|
||||
local animation = Config.Payphones.Animation
|
||||
if not load_model(replacement_hash) or not load_animation(animation.Dictionary) then
|
||||
Bridge.Debug("error", "[sky_phone] The payphone animation assets could not be loaded.")
|
||||
visuals_starting = false
|
||||
SetModelAsNoLongerNeeded(replacement_hash)
|
||||
RemoveAnimDict(animation.Dictionary)
|
||||
return
|
||||
end
|
||||
if active_call_id ~= expected_call_id or active_booth ~= booth or not DoesEntityExist(booth.entity) then
|
||||
visuals_starting = false
|
||||
SetModelAsNoLongerNeeded(replacement_hash)
|
||||
RemoveAnimDict(animation.Dictionary)
|
||||
return
|
||||
end
|
||||
|
||||
local original = active_booth.entity
|
||||
local original = booth.entity
|
||||
local coords = GetEntityCoords(original)
|
||||
local rotation = GetEntityRotation(original, 2)
|
||||
local replacement = CreateObjectNoOffset(
|
||||
@@ -112,7 +138,9 @@ local function start_call_visuals()
|
||||
)
|
||||
if replacement == 0 or not DoesEntityExist(replacement) then
|
||||
Bridge.Debug("error", "[sky_phone] The animated payphone replacement prop could not be created.")
|
||||
visuals_starting = false
|
||||
SetModelAsNoLongerNeeded(replacement_hash)
|
||||
RemoveAnimDict(animation.Dictionary)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -133,6 +161,12 @@ local function start_call_visuals()
|
||||
rotation.z,
|
||||
2
|
||||
)
|
||||
if not animation_scene or animation_scene == -1 then
|
||||
Bridge.Debug("error", "[sky_phone] The payphone synchronized scene could not be created.")
|
||||
animation_scene = nil
|
||||
stop_call_visuals()
|
||||
return
|
||||
end
|
||||
SetSynchronizedSceneHoldLastFrame(animation_scene, true)
|
||||
TaskSynchronizedScene(
|
||||
ped,
|
||||
@@ -147,6 +181,7 @@ local function start_call_visuals()
|
||||
0
|
||||
)
|
||||
PlayEntityAnim(replacement, animation.PropClip, animation.Dictionary, 8.0, false, true, false, 0.0, 0)
|
||||
visuals_starting = false
|
||||
SetModelAsNoLongerNeeded(replacement_hash)
|
||||
end
|
||||
|
||||
@@ -159,14 +194,74 @@ local function booth_payload(booth)
|
||||
end
|
||||
|
||||
local function close_payphone()
|
||||
local was_open = payphone_open
|
||||
payphone_open = false
|
||||
SetNuiFocus(false, false)
|
||||
SendNUIMessage({ type = "payphone:close" })
|
||||
if was_open then
|
||||
SetNuiFocus(false, false)
|
||||
SendNUIMessage({ type = "payphone:close" })
|
||||
end
|
||||
if not active_call_id then
|
||||
active_booth = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function format_duration(seconds)
|
||||
local duration = math.max(0, math.floor(tonumber(seconds) or 0))
|
||||
return ("%02d:%02d"):format(math.floor(duration / 60), duration % 60)
|
||||
end
|
||||
|
||||
local function replace_placeholder(value, placeholder, replacement)
|
||||
return value:gsub("{" .. placeholder .. "}", tostring(replacement))
|
||||
end
|
||||
|
||||
local function current_call_elapsed_seconds()
|
||||
if active_call_state ~= "connected" then
|
||||
return 0
|
||||
end
|
||||
return active_call_elapsed_seconds
|
||||
+ math.max(0, math.floor((GetGameTimer() - active_call_elapsed_updated_at) / 1000))
|
||||
end
|
||||
|
||||
local function call_help_message()
|
||||
local locale = get_locale().Payphone
|
||||
local message
|
||||
if active_call_state == "connected" then
|
||||
local elapsed_seconds = current_call_elapsed_seconds()
|
||||
message = locale.ConnectedHelp
|
||||
message = replace_placeholder(message, "duration", format_duration(elapsed_seconds))
|
||||
message = replace_placeholder(message, "currency", Config.Payphones.Currency)
|
||||
message = replace_placeholder(message, "cost", elapsed_seconds * (tonumber(Config.Payphones.PricePerSecond) or 0))
|
||||
else
|
||||
message = locale.RingingHelp
|
||||
end
|
||||
return replace_placeholder(message, "number", active_call_number or "")
|
||||
end
|
||||
|
||||
local function apply_active_call_state(data)
|
||||
if active_call_id ~= data.id then
|
||||
hangup_requested = false
|
||||
end
|
||||
active_call_id = data.id
|
||||
active_call_state = data.state
|
||||
active_call_number = data.otherNumber or active_call_number
|
||||
if data.state == "connected" then
|
||||
active_call_elapsed_seconds = math.max(0, math.floor(tonumber(data.elapsedSeconds) or 0))
|
||||
active_call_elapsed_updated_at = GetGameTimer()
|
||||
else
|
||||
active_call_elapsed_seconds = 0
|
||||
active_call_elapsed_updated_at = 0
|
||||
end
|
||||
end
|
||||
|
||||
local function clear_active_call_state()
|
||||
active_call_id = nil
|
||||
active_call_state = nil
|
||||
active_call_number = nil
|
||||
active_call_elapsed_seconds = 0
|
||||
active_call_elapsed_updated_at = 0
|
||||
hangup_requested = false
|
||||
end
|
||||
|
||||
local function open_payphone(booth)
|
||||
if payphone_open or active_call_id or IsNuiFocused() then
|
||||
return
|
||||
@@ -193,12 +288,16 @@ RegisterNUICallback("payphone:dial", function(data, cb)
|
||||
local payload = booth_payload(active_booth)
|
||||
payload.phoneNumber = type(data) == "table" and data.phoneNumber or nil
|
||||
local result = Bridge.Callbacks.Trigger("sky_phone:payphone:dial", payload)
|
||||
if result and result.success and result.data and (result.data.state == "ringing" or result.data.state == "connected") then
|
||||
active_call_id = result.data.id
|
||||
distance_hangup_requested = false
|
||||
start_call_visuals()
|
||||
local call_started = result and result.success and result.data
|
||||
and (result.data.state == "ringing" or result.data.state == "connected")
|
||||
if call_started then
|
||||
apply_active_call_state(result.data)
|
||||
end
|
||||
cb(result or { success = false, error = "request_failed" })
|
||||
if call_started then
|
||||
close_payphone()
|
||||
start_call_visuals()
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNUICallback("payphone:hangup", function(_, cb)
|
||||
@@ -223,19 +322,20 @@ RegisterNetEvent("sky_phone:payphone:state", function(data)
|
||||
return
|
||||
end
|
||||
if data.state == "ringing" or data.state == "connected" then
|
||||
active_call_id = data.id
|
||||
distance_hangup_requested = false
|
||||
apply_active_call_state(data)
|
||||
close_payphone()
|
||||
start_call_visuals()
|
||||
if data.state == "connected" and data.channel and call_channel ~= tonumber(data.channel)
|
||||
and not join_call_voice(data.channel)
|
||||
then
|
||||
hangup_requested = true
|
||||
Bridge.Callbacks.Trigger("sky_phone:payphone:hangup", { id = active_call_id })
|
||||
end
|
||||
else
|
||||
active_call_id = nil
|
||||
distance_hangup_requested = false
|
||||
clear_active_call_state()
|
||||
leave_call_voice()
|
||||
stop_call_visuals()
|
||||
active_booth = nil
|
||||
end
|
||||
SendNUIMessage({ type = "payphone:state", data = data })
|
||||
end)
|
||||
@@ -278,9 +378,7 @@ end)
|
||||
CreateThread(function()
|
||||
while true do
|
||||
if nearest_payphone and nearest_payphone.distance <= Config.Payphones.InteractionDistance and not IsNuiFocused() then
|
||||
BeginTextCommandDisplayHelp("STRING")
|
||||
AddTextComponentSubstringPlayerName(get_locale().Payphone.Interact)
|
||||
EndTextCommandDisplayHelp(0, false, true, -1)
|
||||
Bridge.Framework.ShowHelpNotification(get_locale().Payphone.Interact, "E")
|
||||
if IsControlJustReleased(0, 38) then
|
||||
open_payphone(nearest_payphone)
|
||||
end
|
||||
@@ -293,15 +391,21 @@ end)
|
||||
|
||||
CreateThread(function()
|
||||
while true do
|
||||
if active_call_id and active_booth and not distance_hangup_requested then
|
||||
local distance = #(GetEntityCoords(PlayerPedId()) - active_booth.coords)
|
||||
if distance > Config.Payphones.MaximumCallDistance then
|
||||
distance_hangup_requested = true
|
||||
if active_call_id and active_booth then
|
||||
Bridge.Framework.ShowHelpNotification(call_help_message(), "E")
|
||||
if IsControlJustReleased(0, 38) and not hangup_requested then
|
||||
hangup_requested = true
|
||||
Bridge.Callbacks.Trigger("sky_phone:payphone:hangup", { id = active_call_id })
|
||||
end
|
||||
Wait(500)
|
||||
|
||||
local distance = #(GetEntityCoords(PlayerPedId()) - active_booth.coords)
|
||||
if distance > Config.Payphones.MaximumCallDistance and not hangup_requested then
|
||||
hangup_requested = true
|
||||
Bridge.Callbacks.Trigger("sky_phone:payphone:hangup", { id = active_call_id })
|
||||
end
|
||||
Wait(0)
|
||||
else
|
||||
Wait(1000)
|
||||
Wait(250)
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -315,4 +419,5 @@ AddEventHandler("onResourceStop", function(resource_name)
|
||||
end
|
||||
leave_call_voice()
|
||||
stop_call_visuals()
|
||||
clear_active_call_state()
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user