227 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local progressRes
 | 
						|
local isStopping = false
 | 
						|
 | 
						|
FM.progress = {}
 | 
						|
 | 
						|
---@class Anim
 | 
						|
---@field anim string
 | 
						|
---@field dict string
 | 
						|
---@field position? vector3
 | 
						|
---@field rotation? vector3
 | 
						|
---@field enterSpeed? number
 | 
						|
---@field exitSpeed? number
 | 
						|
---@field duration? number
 | 
						|
---@field flag? number
 | 
						|
---@field animTime? number
 | 
						|
 | 
						|
---@class Prop
 | 
						|
---@field model string | number
 | 
						|
---@field position vector3
 | 
						|
---@field rotation vector3
 | 
						|
---@field bone? number
 | 
						|
 | 
						|
---@class fmProgressProps
 | 
						|
---@field label? string
 | 
						|
---@field time? number
 | 
						|
---@field type? 'linear' | 'circle'
 | 
						|
---@field failedLabel? string
 | 
						|
---@field completedLabel? string
 | 
						|
---@field canCancel? boolean
 | 
						|
---@field anim? Anim
 | 
						|
---@field prop? Prop
 | 
						|
---@field scenario? { name: string, position?: vector3, heading?: number, duration?: number, sitting?: boolean, teleport?: boolean }
 | 
						|
---@field disable? { move?: boolean, car?: boolean, combat?: boolean, mouse?: boolean } -- Credits to ox_lib
 | 
						|
---@field useSfx? boolean
 | 
						|
 | 
						|
---@type fmProgressProps | nil
 | 
						|
local currProps
 | 
						|
 | 
						|
-- CREDITS TO ox_lib FOR THE CONTROLS
 | 
						|
local controls = {
 | 
						|
    INPUT_LOOK_LR = 1,
 | 
						|
    INPUT_LOOK_UD = 2,
 | 
						|
    INPUT_SPRINT = 21,
 | 
						|
    INPUT_AIM = 25,
 | 
						|
    INPUT_MOVE_LR = 30,
 | 
						|
    INPUT_MOVE_UD = 31,
 | 
						|
    INPUT_DUCK = 36,
 | 
						|
    INPUT_VEH_MOVE_LEFT_ONLY = 63,
 | 
						|
    INPUT_VEH_MOVE_RIGHT_ONLY = 64,
 | 
						|
    INPUT_VEH_ACCELERATE = 71,
 | 
						|
    INPUT_VEH_BRAKE = 72,
 | 
						|
    INPUT_VEH_EXIT = 75,
 | 
						|
    INPUT_VEH_MOUSE_CONTROL_OVERRIDE = 106
 | 
						|
}
 | 
						|
 | 
						|
local function initProgressThread()
 | 
						|
    while progressRes ~= nil do
 | 
						|
        if currProps.disable then
 | 
						|
            if currProps.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 currProps.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 currProps.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 currProps.disable.combat then
 | 
						|
                DisableControlAction(0, controls.INPUT_AIM, true)
 | 
						|
                DisablePlayerFiring(PlayerId(), true)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        Wait(0)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function stopCurrentAnim()
 | 
						|
    if currProps.anim and currProps.anim.anim and currProps.anim.dict then
 | 
						|
        if IsEntityPlayingAnim(PlayerPedId(), currProps.anim.dict, currProps.anim.anim, 3) then
 | 
						|
            ClearPedTasks(PlayerPedId())
 | 
						|
        end
 | 
						|
        RemoveAnimDict(currProps.anim.dict)
 | 
						|
    elseif currProps.scenario and IsPedUsingScenario(PlayerPedId(), currProps.scenario.name) then
 | 
						|
        ClearPedTasks(PlayerPedId())
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function createProp(prop)
 | 
						|
    if type(prop.position) ~= 'vector3' and type(prop.position) ~= 'table' then FM.console.err('Invalid position') return end
 | 
						|
    if type(prop.rotation) ~= 'vector3' and type(prop.rotation) ~= 'table' then FM.console.err('Invalid rotation') return end
 | 
						|
    if not tonumber(prop.model) then prop.model = joaat(prop.model) end
 | 
						|
    if not IsModelValid(prop.model) then FM.console.err('Invalid model: '..prop.model) return end
 | 
						|
 | 
						|
    if not HasModelLoaded(prop.model) then
 | 
						|
        RequestModel(prop.model)
 | 
						|
        while not HasModelLoaded(prop.model) do Wait(200) end
 | 
						|
    end
 | 
						|
 | 
						|
    local ped = PlayerPedId()
 | 
						|
    local pCoords = GetEntityCoords(ped)
 | 
						|
    local obj = CreateObject(prop.model, pCoords, true, true, true)
 | 
						|
 | 
						|
    AttachEntityToEntity(obj, ped, GetPedBoneIndex(ped, prop.bone or 60309), prop.position, prop.rotation, true, true, false, true, 0, true)
 | 
						|
    SetModelAsNoLongerNeeded(prop.model)
 | 
						|
 | 
						|
    return obj
 | 
						|
end
 | 
						|
 | 
						|
local function setDefaultProps(props)
 | 
						|
    if not props then props = {} end
 | 
						|
    props.label = props.label or 'Progress'
 | 
						|
    props.time = props.time or 3000
 | 
						|
    props.type = props.type or 'linear'
 | 
						|
    props.completedLabel = props.completedLabel or 'Completed'
 | 
						|
    props.failedLabel = props.failedLabel or 'Failed'
 | 
						|
    if props.canCancel == nil then props.canCancel = true end
 | 
						|
    if props.useSfx == nil then props.useSfx = true end
 | 
						|
    if not props.anim and props.scenario then
 | 
						|
        props.scenario.position = props.scenario.position or GetEntityCoords(PlayerPedId())
 | 
						|
        props.scenario.heading = props.scenario.heading or GetEntityHeading(PlayerPedId())
 | 
						|
        props.scenario.duration = props.scenario.duration or -1
 | 
						|
        if props.scenario.sitting == nil then props.scenario.sitting = false end
 | 
						|
        if props.scenario.teleport == nil then props.scenario.teleport = false end
 | 
						|
    end
 | 
						|
    if not props.disable then props.disable = {} end
 | 
						|
 | 
						|
    return props
 | 
						|
end
 | 
						|
 | 
						|
---@param props fmProgressProps | nil
 | 
						|
function FM.progress.start(props)
 | 
						|
    if progressRes ~= nil or isStopping then return FM.console.err('Progress already active') end
 | 
						|
 | 
						|
    currProps = setDefaultProps(props)
 | 
						|
 | 
						|
    local override, res = OverrideProgress(currProps)
 | 
						|
    if override then return res end
 | 
						|
 | 
						|
    progressRes = promise.new()
 | 
						|
    isStopping = false
 | 
						|
 | 
						|
    SendNUIMessage({
 | 
						|
        action = 'startProgress',
 | 
						|
        data = currProps
 | 
						|
    })
 | 
						|
 | 
						|
    if currProps.anim then FM.anim.play(currProps.anim) elseif
 | 
						|
    currProps.scenario then TaskStartScenarioAtPosition(PlayerPedId(), currProps.scenario.name, currProps.scenario.position, currProps.scenario.heading, currProps.scenario.duration, currProps.scenario.sitting, currProps.scenario.teleport) end
 | 
						|
 | 
						|
    if currProps.prop then currProps.prop = createProp(currProps.prop) end
 | 
						|
    CreateThread(initProgressThread)
 | 
						|
 | 
						|
    return Citizen.Await(progressRes)
 | 
						|
end
 | 
						|
 | 
						|
---@param success boolean
 | 
						|
function FM.progress.stop(success)
 | 
						|
    if StopOverrideProgress() then return end
 | 
						|
    if progressRes == nil then return FM.console.err('No progress active') end
 | 
						|
    if isStopping then return FM.console.err('Progress already stopping') end
 | 
						|
    isStopping = true
 | 
						|
 | 
						|
    SendNUIMessage({
 | 
						|
        action = 'stopProgress',
 | 
						|
        data = success
 | 
						|
    })
 | 
						|
end
 | 
						|
 | 
						|
RegisterNUICallback('progressStopped', function(success, cb)
 | 
						|
    isStopping = true
 | 
						|
 | 
						|
    if progressRes then
 | 
						|
        progressRes:resolve(success)
 | 
						|
        progressRes = nil
 | 
						|
 | 
						|
        if currProps.anim or currProps.scenario then stopCurrentAnim() end
 | 
						|
        if currProps.prop then DeleteEntity(currProps.prop) end
 | 
						|
 | 
						|
        currProps = nil
 | 
						|
    end
 | 
						|
 | 
						|
    isStopping = false
 | 
						|
    cb(true)
 | 
						|
end)
 | 
						|
 | 
						|
---@return boolean
 | 
						|
function FM.progress.isActive()
 | 
						|
    return progressRes ~= nil
 | 
						|
end
 | 
						|
 | 
						|
RegisterCommand('progresscancel', function(source, args, raw)
 | 
						|
    if isStopping or progressRes == nil or not currProps or not currProps.canCancel then return end
 | 
						|
    FM.progress.stop(false)
 | 
						|
end)
 | 
						|
RegisterKeyMapping('progresscancel', 'Cancel Progress', KeyMappings.CANCEL.mapper, KeyMappings.CANCEL.key)
 | 
						|
 | 
						|
--[[ EXAMPLE FOR NOW HERE ]]
 | 
						|
-- RegisterCommand('startprogress', function(source, args, raw)
 | 
						|
--     if FM.progress.start({
 | 
						|
--         label = 'Testing progress',
 | 
						|
--         time = 10000,
 | 
						|
--         canCancel = true,
 | 
						|
--         type = 'linear',
 | 
						|
--         failedLabel = 'Progress Failed!',
 | 
						|
--         completedLabel = 'Progress Completed!',
 | 
						|
--         anim = { dict = "amb@world_human_gardener_plant@male@base", anim = "base" },
 | 
						|
--         prop = { model = 'prop_tool_broom', position = vector3(0.0, 0.0, 0.0), rotation = vector3(0.0, 0.0, 0.0), bone = 60309 },
 | 
						|
--     }) then
 | 
						|
--         FM.console.debug('Progress success')
 | 
						|
--     else
 | 
						|
--         FM.console.debug('Progress failed')
 | 
						|
--     end
 | 
						|
-- end)
 | 
						|
 |