Progress bar system with animations, props, and control disables
The Progress component displays progress bars for timed actions like crafting, lockpicking, eating, and more. It supports animations, props, control disabling, and cancellation.
-- Lock player in place during actioncontrolDisables = { disableMovement = true, disableCarMovement = true, disableMouse = false, -- Allow looking around disableCombat = true,}
prop = { model = 'prop_phone_01', bone = 57005, -- Bone index (default: 60309 = right hand) coords = { x = 0.14, y = 0.01, z = 0.02 }, rotation = { x = 110.0, y = 120.0, z = 0.0 },}
-- Left and right handsprop = { model = 'prop_cs_burger_01', bone = 60309, -- Right hand coords = { x = 0.0, y = 0.0, z = 0.0 }, rotation = { x = 0.0, y = 0.0, z = 0.0 },},propTwo = { model = 'prop_drink_champ', bone = 18905, -- Left hand coords = { x = 0.0, y = 0.0, z = 0.0 }, rotation = { x = 0.0, y = 0.0, z = 0.0 },}
Common Bone Indices:
60309 - Right hand (default)
18905 - Left hand
28422 - Left foot
52301 - Right foot
24818 - Head
11816 - Pelvis
Prop Examples:
-- Eating burgerprop = { model = 'prop_cs_burger_01', bone = 60309, coords = { x = 0.0, y = 0.0, z = -0.02 }, rotation = { x = 0.0, y = 0.0, z = 0.0 },}-- Drinking waterprop = { model = 'prop_ld_flow_bottle', bone = 60309, coords = { x = 0.03, y = 0.02, z = 0.02 }, rotation = { x = 240.0, y = -60.0, z = 0.0 },}-- Holding lockpickprop = { model = 'prop_tool_screwdvr01', bone = 28422, coords = { x = 0.0, y = 0.0, z = 0.0 }, rotation = { x = 0.0, y = 0.0, z = 0.0 },}
-- Client side-- Fail if player loses required item mid-actionAddEventHandler('Inventory:Client:ItemRemoved', function(item) local action = Progress:CurrentAction() if action == 'using_lockpick' and item.name == 'lockpick' then Progress:Fail() endend)
-- Client sideAddEventHandler('ems:client:Revive', function(targetId) local targetPed = GetPlayerPed(GetPlayerFromServerId(targetId)) local distance = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(targetPed)) if distance > 3.0 then Notification:Error('Too far away') return end Progress:Progress({ name = 'reviving_player', duration = 10000, label = 'Reviving patient...', useWhileDead = false, canCancel = false, -- Cannot cancel revive controlDisables = { disableMovement = true, disableCombat = true, }, animation = { animDict = 'amb@medic@standing@kneel@base', anim = 'base', flags = 1, }, prop = { model = 'prop_ld_health_pack', bone = 60309, coords = { x = 0.0, y = 0.0, z = 0.0 }, rotation = { x = 0.0, y = 0.0, z = 0.0 }, }, }, function(cancelled) if not cancelled then -- Check if still close enough local newDist = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(targetPed)) if newDist <= 3.0 then TriggerServerEvent('ems:server:RevivePlayer', targetId) else Notification:Error('Patient moved too far') end end end)end)
-- ❌ BAD - Trust clientAddEventHandler('crafting:server:Complete', function(recipeId) -- Client says they completed crafting local src = source GiveItem(src, recipeId) -- UNSAFE!end)-- ✅ GOOD - Validate on serverAddEventHandler('crafting:server:Complete', function(recipeId) local src = source local player = Fetch:Source(src) if not player then return end local char = player:GetData('Character') local recipe = GetRecipe(recipeId) -- Verify player still has materials local stateId = char:GetData('SID') -- Check all materials local hasMaterials = true for _, mat in ipairs(recipe.materials) do if not Inventory.Items:Has(stateId, 1, mat.item, mat.count) then hasMaterials = false break end end if hasMaterials then -- Remove materials for _, mat in ipairs(recipe.materials) do Inventory.Items:Remove(stateId, 1, mat.item, mat.count) end -- Give result Inventory:AddItem(stateId, recipe.result, 1) endend)
-- Check distance before AND after progresslocal startCoords = GetEntityCoords(PlayerPedId())Progress:Progress({ -- ... config}, function(cancelled) if not cancelled then local endCoords = GetEntityCoords(PlayerPedId()) local distance = #(startCoords - endCoords) if distance > 5.0 then Notification:Error('You moved too far') return end -- Continue with action TriggerServerEvent('action:server:Complete') endend)