2025-04-07 01:41:12 +00:00
|
|
|
local config = require 'config.client'
|
|
|
|
|
local playerState = LocalPlayer.state
|
|
|
|
|
local speedMultiplier = config.useMPH and 2.237 or 3.6
|
|
|
|
|
local minSpeeds = {
|
|
|
|
|
unbuckled = config.minSpeedUnbuckled / speedMultiplier,
|
|
|
|
|
buckled = config.minSpeedBuckled / speedMultiplier,
|
|
|
|
|
harness = config.harness.minSpeed / speedMultiplier
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-- Functions
|
2025-04-14 10:33:35 +00:00
|
|
|
local function playBuckleSound()
|
|
|
|
|
local maxAttempts = 3
|
|
|
|
|
local attempt = 0
|
|
|
|
|
local success = false
|
|
|
|
|
|
|
|
|
|
while attempt < maxAttempts and not success do
|
|
|
|
|
attempt = attempt + 1
|
|
|
|
|
success = lib.waitFor(function()
|
|
|
|
|
return lib.loadAudioBank('audiodirectory/seatbelt_sounds')
|
|
|
|
|
end, 'Failed to load seatbelt audio bank', 1000)
|
|
|
|
|
|
|
|
|
|
if success then
|
|
|
|
|
if IsSoundPlayed then
|
|
|
|
|
StopSound(IsSoundPlayed)
|
|
|
|
|
end
|
|
|
|
|
PlaySoundFrontend(-1, buckle and "Buckle" or "Unbuckle", "Seatbelt_SoundSet", true)
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if attempt < maxAttempts then
|
|
|
|
|
Wait(500) -- Wait before retry
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not success then
|
|
|
|
|
print('^3Warning: Could not load seatbelt sound after ' .. maxAttempts .. ' attempts^7')
|
|
|
|
|
return false
|
|
|
|
|
end
|
2025-04-07 01:41:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function toggleSeatbelt()
|
2025-04-14 10:33:35 +00:00
|
|
|
if not IsPedInAnyVehicle(cache.ped, false) then return end
|
|
|
|
|
|
|
|
|
|
buckle = not buckle
|
|
|
|
|
|
|
|
|
|
-- Attempt to play sound but continue even if it fails
|
|
|
|
|
local soundPlayed = playBuckleSound()
|
|
|
|
|
if not soundPlayed then
|
|
|
|
|
-- Continue with seatbelt logic even if sound failed
|
|
|
|
|
print('^3Warning: Proceeding without seatbelt sound^7')
|
2025-04-07 01:41:12 +00:00
|
|
|
end
|
2025-04-14 10:33:35 +00:00
|
|
|
|
|
|
|
|
SetFlyThroughWindscreenParams(windscreenDamageOffset, windscreenDamageMult, beltOffset, beltMult, buckle and 1.0 or 0.0)
|
|
|
|
|
SendNUIMessage({ action = 'seatbelt', value = buckle })
|
2025-04-07 01:41:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function toggleHarness()
|
|
|
|
|
local harnessOn = not playerState.harness
|
|
|
|
|
playerState.harness = harnessOn
|
|
|
|
|
TriggerEvent('seatbelt:client:ToggleSeatbelt')
|
2025-04-14 10:33:35 +00:00
|
|
|
playBuckleSound()
|
2025-04-07 01:41:12 +00:00
|
|
|
|
|
|
|
|
local canFlyThroughWindscreen = not harnessOn
|
|
|
|
|
if config.harness.disableFlyingThroughWindscreen then
|
|
|
|
|
SetPedConfigFlag(cache.ped, 32, canFlyThroughWindscreen) -- PED_FLAG_CAN_FLY_THRU_WINDSCREEN
|
|
|
|
|
else
|
|
|
|
|
local minSpeed = harnessOn and minSpeeds.harness or (playerState.seatbelt and minSpeeds.buckled or minSpeeds.unbuckled)
|
|
|
|
|
SetFlyThroughWindscreenParams(minSpeed, 1.0, 17.0, 10.0)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function seatbelt()
|
|
|
|
|
while cache.vehicle do
|
|
|
|
|
local sleep = 1000
|
|
|
|
|
if playerState.seatbelt or playerState.harness then
|
|
|
|
|
sleep = 0
|
|
|
|
|
DisableControlAction(0, 75, true)
|
|
|
|
|
DisableControlAction(27, 75, true)
|
|
|
|
|
end
|
|
|
|
|
Wait(sleep)
|
|
|
|
|
end
|
|
|
|
|
playerState.seatbelt = false
|
|
|
|
|
playerState.harness = false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Export
|
|
|
|
|
function HasHarness()
|
|
|
|
|
return playerState.harness
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- @deprecated Use `state.seatbelt` instead
|
|
|
|
|
exports('HasHarness', HasHarness)
|
|
|
|
|
|
|
|
|
|
-- Main Thread
|
|
|
|
|
CreateThread(function()
|
|
|
|
|
SetFlyThroughWindscreenParams(minSpeeds.unbuckled, 1.0, 17.0, 10.0)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
lib.onCache('vehicle', function()
|
|
|
|
|
Wait(500)
|
|
|
|
|
seatbelt()
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
-- Events
|
|
|
|
|
RegisterNetEvent('qbx_seatbelt:client:UseHarness', function(ItemData)
|
|
|
|
|
if playerState.seatbelt then
|
|
|
|
|
exports.qbx_core:Notify(locale('error.seatbelton'), 'error')
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local class = GetVehicleClass(cache.vehicle)
|
|
|
|
|
|
|
|
|
|
if not cache.vehicle or class == 8 or class == 13 or class == 14 then
|
|
|
|
|
exports.qbx_core:Notify(locale('notify.notInCar'), 'error')
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not playerState.harness then
|
|
|
|
|
if lib.progressCircle({
|
|
|
|
|
duration = 5000,
|
|
|
|
|
label = locale('progress.attachHarness'),
|
|
|
|
|
position = 'bottom',
|
|
|
|
|
useWhileDead = false,
|
|
|
|
|
canCancel = true,
|
|
|
|
|
disable = {
|
|
|
|
|
combat = true
|
|
|
|
|
}
|
|
|
|
|
}) then
|
|
|
|
|
TriggerServerEvent('qbx_seatbelt:server:equip', ItemData.slot)
|
|
|
|
|
toggleHarness()
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
if lib.progressCircle({
|
|
|
|
|
duration = 5000,
|
|
|
|
|
label = locale('progress.removeHarness'),
|
|
|
|
|
position = 'bottom',
|
|
|
|
|
useWhileDead = false,
|
|
|
|
|
canCancel = true,
|
|
|
|
|
disable = {
|
|
|
|
|
combat = true
|
|
|
|
|
}
|
|
|
|
|
}) then
|
|
|
|
|
toggleHarness()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
-- Register Key
|
|
|
|
|
lib.addKeybind({
|
|
|
|
|
name = 'toggleseatbelt',
|
|
|
|
|
description = locale('toggleCommand'),
|
|
|
|
|
defaultKey = config.keybind,
|
|
|
|
|
onPressed = function()
|
|
|
|
|
if not cache.vehicle or IsPauseMenuActive() then return end
|
|
|
|
|
local class = GetVehicleClass(cache.vehicle)
|
|
|
|
|
if class == 8 or class == 13 or class == 14 then return end
|
|
|
|
|
toggleSeatbelt()
|
|
|
|
|
end
|
|
|
|
|
})
|