Files
ox_lib/resource/interface/client/progress.lua

284 lines
8.5 KiB
Lua
Raw Normal View History

--[[
https://github.com/overextended/ox_lib
This file is licensed under LGPL-3.0 or higher <https://www.gnu.org/licenses/lgpl-3.0.en.html>
2025-03-29 21:44:03 +11:00
Copyright © 2025 Linden <https://github.com/thelindat>
]]
2022-04-14 06:45:48 +00:00
local progress
local DisableControlAction = DisableControlAction
local DisablePlayerFiring = DisablePlayerFiring
local playerState = LocalPlayer.state
local createdProps = {}
local maxProps = GetConvarInt('ox:progressPropLimit', 2)
2022-04-14 06:45:48 +00:00
---@class ProgressPropProps
---@field model string
---@field bone? number
---@field pos vector3
---@field rot vector3
---@field rotOrder? number
---@class ProgressProps
---@field label? string
---@field duration number
---@field position? 'middle' | 'bottom'
---@field useWhileDead? boolean
---@field allowRagdoll? boolean
---@field allowCuffed? boolean
---@field allowFalling? boolean
---@field allowSwimming? boolean
---@field canCancel? boolean
---@field anim? { dict?: string, clip: string, flag?: number, blendIn?: number, blendOut?: number, duration?: number, playbackRate?: number, lockX?: boolean, lockY?: boolean, lockZ?: boolean, scenario?: string, playEnter?: boolean }
---@field prop? ProgressPropProps | ProgressPropProps[]
---@field disable? { move?: boolean, sprint?: boolean, car?: boolean, combat?: boolean, mouse?: boolean }
local function createProp(ped, prop)
local ok, result = pcall(lib.requestModel, prop.model)
if not ok then return lib.print.error(result) end
local coords = GetEntityCoords(ped)
local object = CreateObject(result, coords.x, coords.y, coords.z, false, false, false)
2025-03-07 15:20:24 +11:00
AttachEntityToEntity(object, ped, GetPedBoneIndex(ped, prop.bone or 60309), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true,
true, false, true, prop.rotOrder or 0, true)
SetModelAsNoLongerNeeded(result)
return object
end
local function interruptProgress(data)
if not data.useWhileDead and IsEntityDead(cache.ped) then return true end
if not data.allowRagdoll and IsPedRagdoll(cache.ped) then return true end
if not data.allowCuffed and IsPedCuffed(cache.ped) then return true end
if not data.allowFalling and IsPedFalling(cache.ped) then return true end
if not data.allowSwimming and IsPedSwimming(cache.ped) then return true end
end
local isFivem = cache.game == 'fivem'
local controls = {
INPUT_LOOK_LR = isFivem and 1 or 0xA987235F,
INPUT_LOOK_UD = isFivem and 2 or 0xD2047988,
INPUT_SPRINT = isFivem and 21 or 0x8FFC75D6,
INPUT_AIM = isFivem and 25 or 0xF84FA74F,
INPUT_MOVE_LR = isFivem and 30 or 0x4D8FB4C1,
INPUT_MOVE_UD = isFivem and 31 or 0xFDA83190,
INPUT_DUCK = isFivem and 36 or 0xDB096B85,
INPUT_VEH_MOVE_LEFT_ONLY = isFivem and 63 or 0x9DF54706,
INPUT_VEH_MOVE_RIGHT_ONLY = isFivem and 64 or 0x97A8FD98,
INPUT_VEH_ACCELERATE = isFivem and 71 or 0x5B9FD4E2,
INPUT_VEH_BRAKE = isFivem and 72 or 0x6E1F639B,
INPUT_VEH_EXIT = isFivem and 75 or 0xFEFAB9B4,
INPUT_VEH_MOUSE_CONTROL_OVERRIDE = isFivem and 106 or 0x39CCABD5
}
2025-03-07 15:20:24 +11:00
---@param data ProgressProps
2022-04-14 06:45:48 +00:00
local function startProgress(data)
playerState.invBusy = true
progress = data
local anim = data.anim
if anim then
if anim.dict then
lib.requestAnimDict(anim.dict)
2025-03-07 15:20:24 +11:00
TaskPlayAnim(cache.ped, anim.dict, anim.clip, anim.blendIn or 3.0, anim.blendOut or 1.0, anim.duration or -1, anim.flag or 49, anim.playbackRate or 0,
anim.lockX, anim.lockY, anim.lockZ)
RemoveAnimDict(anim.dict)
elseif anim.scenario then
2025-03-07 15:20:24 +11:00
TaskStartScenarioInPlace(cache.ped, anim.scenario, 0, anim.playEnter == nil or anim.playEnter --[[@as boolean]])
end
end
if data.prop then
TriggerServerEvent('ox_lib:progressProps', data.prop)
end
local disable = data.disable
2025-03-07 15:20:24 +11:00
local startTime = GetGameTimer()
while progress do
if disable then
if disable.mouse then
DisableControlAction(0, controls.INPUT_LOOK_LR, true)
DisableControlAction(0, controls.INPUT_LOOK_UD, true)
DisableControlAction(0, controls.INPUT_VEH_MOUSE_CONTROL_OVERRIDE, true)
end
if disable.move then
DisableControlAction(0, controls.INPUT_SPRINT, true)
DisableControlAction(0, controls.INPUT_MOVE_LR, true)
DisableControlAction(0, controls.INPUT_MOVE_UD, true)
DisableControlAction(0, controls.INPUT_DUCK, true)
end
if disable.sprint and not disable.move then
DisableControlAction(0, controls.INPUT_SPRINT, true)
end
if disable.car then
DisableControlAction(0, controls.INPUT_VEH_MOVE_LEFT_ONLY, true)
DisableControlAction(0, controls.INPUT_VEH_MOVE_RIGHT_ONLY, true)
DisableControlAction(0, controls.INPUT_VEH_ACCELERATE, true)
DisableControlAction(0, controls.INPUT_VEH_BRAKE, true)
DisableControlAction(0, controls.INPUT_VEH_EXIT, true)
end
if disable.combat then
DisableControlAction(0, controls.INPUT_AIM, true)
DisablePlayerFiring(cache.playerId, true)
end
end
if interruptProgress(progress) then
progress = false
end
Wait(0)
end
if data.prop then
TriggerServerEvent('ox_lib:progressProps', nil)
end
if anim then
if anim.dict then
StopAnimTask(cache.ped, anim.dict, anim.clip, 1.0)
Wait(0) -- This is needed here otherwise the StopAnimTask is cancelled
else
ClearPedTasks(cache.ped)
end
end
playerState.invBusy = false
local duration = progress ~= false and GetGameTimer() - startTime + 100 -- give slight leeway
if progress == false or duration <= data.duration then
SendNUIMessage({ action = 'progressCancel' })
return false
end
return true
2022-04-14 06:45:48 +00:00
end
---@param data ProgressProps
---@return boolean?
2022-04-14 06:45:48 +00:00
function lib.progressBar(data)
while progress ~= nil do Wait(0) end
if not interruptProgress(data) then
SendNUIMessage({
action = 'progress',
data = {
label = data.label,
duration = data.duration
}
})
return startProgress(data)
end
2022-04-14 06:45:48 +00:00
end
---@param data ProgressProps
---@return boolean?
2022-04-14 06:45:48 +00:00
function lib.progressCircle(data)
while progress ~= nil do Wait(0) end
if not interruptProgress(data) then
SendNUIMessage({
action = 'circleProgress',
data = {
duration = data.duration,
position = data.position,
label = data.label
}
})
return startProgress(data)
end
2022-04-14 06:45:48 +00:00
end
function lib.cancelProgress()
if not progress then
error('No progress bar is active')
end
2022-04-14 06:45:48 +00:00
progress = false
2022-04-14 06:45:48 +00:00
end
---@return boolean
function lib.progressActive()
return progress and true
end
2022-04-14 06:45:48 +00:00
RegisterNUICallback('progressComplete', function(data, cb)
cb(1)
progress = nil
2022-04-14 06:45:48 +00:00
end)
RegisterCommand('cancelprogress', function()
if progress?.canCancel then progress = false end
end)
if isFivem then
RegisterKeyMapping('cancelprogress', locale('cancel_progress'), 'keyboard', 'x')
end
local function deleteProgressProps(serverId)
local playerProps = createdProps[serverId]
if not playerProps then return end
createdProps[serverId] = nil
for i = 1, #playerProps do
local prop = playerProps[i]
if DoesEntityExist(prop) then
DeleteEntity(prop)
end
end
end
RegisterNetEvent('onPlayerDropped', function(serverId)
deleteProgressProps(serverId)
end)
AddStateBagChangeHandler('lib:progressProps', nil, function(bagName, key, value, reserved, replicated)
if replicated then return end
local ply = GetPlayerFromStateBagName(bagName)
if ply == 0 then return end
local ped = GetPlayerPed(ply)
local serverId = GetPlayerServerId(ply)
2025-03-07 15:20:24 +11:00
if not value or createdProps[serverId] then
return deleteProgressProps(serverId)
end
2025-03-07 15:20:24 +11:00
local playerProps = {}
2025-03-07 15:20:24 +11:00
if value.model then
local prop = createProp(ped, value)
if prop then
playerProps[#playerProps + 1] = createProp(ped, value)
end
else
local propCount = math.min(maxProps, #value)
for i = 1, propCount do
local prop = createProp(ped, value[i])
if prop then
playerProps[#playerProps + 1] = prop
end
end
end
createdProps[serverId] = playerProps
end)