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:
Copy
-- 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 = COMPONENTS.Progress:CurrentAction() if action == 'using_lockpick' and item.name == 'lockpick' then COMPONENTS.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 COMPONENTS.Notification:Error('Too far away') return end COMPONENTS.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 COMPONENTS.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 if COMPONENTS.Inventory:HasItems(char:GetData('SID'), recipe.materials) then -- Remove materials for _, mat in ipairs(recipe.materials) do COMPONENTS.Inventory:RemoveItem(char:GetData('SID'), mat.item, mat.count, 1) end -- Give result COMPONENTS.Inventory:AddItem(char:GetData('SID'), recipe.result, 1, {}, 1) endend)
-- Check distance before AND after progresslocal startCoords = GetEntityCoords(PlayerPedId())COMPONENTS.Progress:Progress({ -- ... config}, function(cancelled) if not cancelled then local endCoords = GetEntityCoords(PlayerPedId()) local distance = #(startCoords - endCoords) if distance > 5.0 then COMPONENTS.Notification:Error('You moved too far') return end -- Continue with action TriggerServerEvent('action:server:Complete') endend)