├── README.md
├── client.lua
├── fxmanifest.lua
└── html
├── index.html
├── script.js
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | # Progressbar
2 |
3 | Dependency for creating progressbars in QB-Core.
4 |
5 | # Usage
6 |
7 | ## QB-Core Functions
8 |
9 | ### Client
10 |
11 | - QBCore.Functions.Progressbar(**name**: string, **label**: string, **duration**: number, **useWhileDead**: boolean, **canCancel**: boolean, **disableControls**: table, **animation**: table, **prop**: table, **propTwo**: table, **onFinish**: function, **onCancel**: function)
12 | > Create a new progressbar from the built in qb-core functions.
13 | > **Example:**
14 | > ```lua
15 | >QBCore.Functions.Progressbar("random_task", "Doing something", 5000, false, true, {
16 | > disableMovement = false,
17 | > disableCarMovement = false,
18 | > disableMouse = false,
19 | > disableCombat = true,
20 | >}, {
21 | > animDict = "mp_suicide",
22 | > anim = "pill",
23 | > flags = 49,
24 | >}, {}, {}, function()
25 | > -- Done
26 | >end, function()
27 | > -- Cancel
28 | >end)
29 | > ```
30 |
31 | ## Exports
32 |
33 | ### Client
34 |
35 | - Progress(**data**: string, **handler**: function)
36 | > Creates a new progress bar directly from the export, always use the built in qb-core function if possible.
37 | > **Example:**
38 | > ```lua
39 | >exports['progressbar']:Progress({
40 | > name = "random_task",
41 | > duration = 5000,
42 | > label = "Doing something",
43 | > useWhileDead = false,
44 | > canCancel = true,
45 | > controlDisables = {
46 | > disableMovement = false,
47 | > disableCarMovement = false,
48 | > disableMouse = false,
49 | > disableCombat = true,
50 | > },
51 | > animation = {
52 | > animDict = "mp_suicide",
53 | > anim = "pill",
54 | > flags = 49,
55 | > },
56 | > prop = {},
57 | > propTwo = {}
58 | >}, function(cancelled)
59 | > if not cancelled then
60 | > -- finished
61 | > else
62 | > -- cancelled
63 | > end
64 | >end)
65 | > ```
66 | > **Props Example:**
67 | > ```lua
68 | >exports['progressbar']:Progress({
69 | > name = "random_task",
70 | > duration = 5000,
71 | > label = "Doing something",
72 | > useWhileDead = false,
73 | > canCancel = true,
74 | > controlDisables = {
75 | > disableMovement = false,
76 | > disableCarMovement = false,
77 | > disableMouse = false,
78 | > disableCombat = true,
79 | > },
80 | > animation = {
81 | > animDict = "missheistdockssetup1clipboard@base",
82 | > anim = "pill",
83 | > flags = 49,
84 | > },
85 | > prop = {
86 | > model = 'prop_notepad_01',
87 | > bone = 18905,
88 | > coords = vec3(0.1, 0.02, 0.05),
89 | > rotation = vec3(10.0, 0.0, 0.0),
90 | > },
91 | > propTwo = {
92 | > model = 'prop_pencil_01',
93 | > bone = 58866,
94 | > coords = vec3(0.11, -0.02, 0.001),
95 | > rotation = vec3(-120.0, 0.0, 0.0),
96 | > }
97 | >}, function(cancelled)
98 | > if not cancelled then
99 | > -- finished
100 | > else
101 | > -- cancelled
102 | > end
103 | >end)
104 | > ```
105 |
106 | - isDoingSomething()
107 | > Returns a boolean (true/false) depending on if a progressbar is present.
108 | > **Example:**
109 | > ```lua
110 | > local busy = exports["progressbar"]:isDoingSomething()
111 | > ```
112 |
113 | - ProgressWithStartEvent(**data**: table, **start**: function, **finish**: function)
114 | > Works like a normal progressbar, the data parameter should be the same as the data passed into the `Progress` export above.
115 | > The start function gets triggered upon the start of the progressbar.
116 | > The finish handler is the same as the `handler` parameter in the `Progress` export above.
117 |
118 | - ProgressWithTickEvent(**data**: table, **tick**: function, **finish**: function)
119 | > Works like a normal progressbar, the data parameter should be the same as the data passed into the `Progress` export above.
120 | > The tick function gets triggered every frame while the progressbar is active.
121 | > The finish handler is the same as the `handler` parameter in the `Progress` export above.
122 |
123 | - ProgressWithTickEvent(**data**: table, **start**: function, **tick**: function, **finish**: function)
124 | > Works like a normal progressbar, the data parameter should be the same as the data passed into the `Progress` export above.
125 | > The start function gets triggered upon the start of the progressbar.
126 | > The tick function gets triggered every frame while the progressbar is active.
127 | > The finish handler is the same as the `handler` parameter in the `Progress` export above.
128 |
--------------------------------------------------------------------------------
/client.lua:
--------------------------------------------------------------------------------
1 | local Action = {
2 | name = '',
3 | duration = 0,
4 | label = '',
5 | useWhileDead = false,
6 | canCancel = true,
7 | disarm = true,
8 | controlDisables = {
9 | disableMovement = false,
10 | disableCarMovement = false,
11 | disableMouse = false,
12 | disableCombat = false,
13 | },
14 | animation = {
15 | animDict = nil,
16 | anim = nil,
17 | flags = 0,
18 | task = nil,
19 | },
20 | prop = {
21 | model = nil,
22 | bone = nil,
23 | coords = vec3(0.0, 0.0, 0.0),
24 | rotation = vec3(0.0, 0.0, 0.0),
25 | },
26 | propTwo = {
27 | model = nil,
28 | bone = nil,
29 | coords = vec3(0.0, 0.0, 0.0),
30 | rotation = vec3(0.0, 0.0, 0.0),
31 | },
32 | }
33 |
34 | local isDoingAction = false
35 | local wasCancelled = false
36 | local prop_net = nil
37 | local propTwo_net = nil
38 | local isAnim = false
39 | local isProp = false
40 | local isPropTwo = false
41 |
42 | local controls = {
43 | disableMouse = { 1, 2, 106 },
44 | disableMovement = { 30, 31, 36, 21, 75 },
45 | disableCarMovement = { 63, 64, 71, 72 },
46 | disableCombat = { 24, 25, 37, 47, 58, 140, 141, 142, 143, 263, 264, 257 }
47 | }
48 |
49 | -- Functions
50 |
51 | local function loadAnimDict(dict)
52 | RequestAnimDict(dict)
53 | while not HasAnimDictLoaded(dict) do
54 | Wait(5)
55 | end
56 | end
57 |
58 | local function loadModel(model)
59 | RequestModel(model)
60 | while not HasModelLoaded(model) do
61 | Wait(5)
62 | end
63 | end
64 |
65 | local function createAndAttachProp(prop, ped)
66 | loadModel(prop.model)
67 | local coords = GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.0, 0.0)
68 | local propEntity = CreateObject(GetHashKey(prop.model), coords.x, coords.y, coords.z, true, true, true)
69 | local netId = ObjToNet(propEntity)
70 | SetNetworkIdExistsOnAllMachines(netId, true)
71 | NetworkUseHighPrecisionBlending(netId, true)
72 | SetNetworkIdCanMigrate(netId, false)
73 | local boneIndex = GetPedBoneIndex(ped, prop.bone or 60309)
74 | AttachEntityToEntity(
75 | propEntity, ped, boneIndex,
76 | prop.coords.x, prop.coords.y, prop.coords.z,
77 | prop.rotation.x, prop.rotation.y, prop.rotation.z,
78 | true, true, false, true, 0, true
79 | )
80 | return netId
81 | end
82 |
83 | local function disableControls()
84 | CreateThread(function()
85 | while isDoingAction do
86 | for disableType, isEnabled in pairs(Action.controlDisables) do
87 | if isEnabled and controls[disableType] then
88 | for _, control in ipairs(controls[disableType]) do
89 | DisableControlAction(0, control, true)
90 | end
91 | end
92 | end
93 | if Action.controlDisables.disableCombat then
94 | DisablePlayerFiring(PlayerId(), true)
95 | end
96 | Wait(0)
97 | end
98 | end)
99 | end
100 |
101 | local function StartActions()
102 | local ped = PlayerPedId()
103 | if isDoingAction then
104 | if not isAnim and Action.animation then
105 | if Action.animation.task then
106 | TaskStartScenarioInPlace(ped, Action.animation.task, 0, true)
107 | else
108 | local anim = Action.animation
109 | if anim.animDict and anim.anim and DoesEntityExist(ped) and not IsEntityDead(ped) then
110 | loadAnimDict(anim.animDict)
111 | TaskPlayAnim(ped, anim.animDict, anim.anim, 3.0, 3.0, -1, anim.flags or 1, 0, false, false, false)
112 | end
113 | end
114 | isAnim = true
115 | end
116 | if not isProp and Action.prop and Action.prop.model then
117 | prop_net = createAndAttachProp(Action.prop, ped)
118 | isProp = true
119 | end
120 | if not isPropTwo and Action.propTwo and Action.propTwo.model then
121 | propTwo_net = createAndAttachProp(Action.propTwo, ped)
122 | isPropTwo = true
123 | end
124 | disableControls()
125 | end
126 | end
127 |
128 | local function StartProgress(action, onStart, onTick, onFinish)
129 | local playerPed = PlayerPedId()
130 | local isPlayerDead = IsEntityDead(playerPed)
131 | if (not isPlayerDead or action.useWhileDead) and not isDoingAction then
132 | isDoingAction = true
133 | LocalPlayer.state:set('inv_busy', true, true)
134 | Action = action
135 | SendNUIMessage({
136 | action = 'progress',
137 | duration = action.duration,
138 | label = action.label
139 | })
140 | StartActions()
141 | CreateThread(function()
142 | if onStart then onStart() end
143 | while isDoingAction do
144 | Wait(1)
145 | if onTick then onTick() end
146 | if IsControlJustPressed(0, 200) and action.canCancel then
147 | TriggerEvent('progressbar:client:cancel')
148 | wasCancelled = true
149 | break
150 | end
151 | if IsEntityDead(playerPed) and not action.useWhileDead then
152 | TriggerEvent('progressbar:client:cancel')
153 | wasCancelled = true
154 | break
155 | end
156 | end
157 | if onFinish then onFinish(wasCancelled) end
158 | isDoingAction = false
159 | end)
160 | end
161 | end
162 |
163 | local function ActionCleanup()
164 | local ped = PlayerPedId()
165 | if Action.animation then
166 | if Action.animation.task or (Action.animation.animDict and Action.animation.anim) then
167 | StopAnimTask(ped, Action.animation.animDict, Action.animation.anim, 1.0)
168 | ClearPedSecondaryTask(ped)
169 | else
170 | ClearPedTasks(ped)
171 | end
172 | end
173 | if prop_net then
174 | DetachEntity(NetToObj(prop_net), true, true)
175 | DeleteObject(NetToObj(prop_net))
176 | end
177 | if propTwo_net then
178 | DetachEntity(NetToObj(propTwo_net), true, true)
179 | DeleteObject(NetToObj(propTwo_net))
180 | end
181 | prop_net = nil
182 | propTwo_net = nil
183 | isDoingAction = false
184 | wasCancelled = false
185 | isAnim = false
186 | isProp = false
187 | isPropTwo = false
188 | LocalPlayer.state:set('inv_busy', false, true)
189 | end
190 |
191 | -- Events
192 |
193 | RegisterNetEvent('progressbar:client:ToggleBusyness', function(bool)
194 | isDoingAction = bool
195 | end)
196 |
197 | RegisterNetEvent('progressbar:client:progress', function(action, finish)
198 | StartProgress(action, nil, nil, finish)
199 | end)
200 |
201 | RegisterNetEvent('progressbar:client:ProgressWithStartEvent', function(action, start, finish)
202 | StartProgress(action, start, nil, finish)
203 | end)
204 |
205 | RegisterNetEvent('progressbar:client:ProgressWithTickEvent', function(action, tick, finish)
206 | StartProgress(action, nil, tick, finish)
207 | end)
208 |
209 | RegisterNetEvent('progressbar:client:ProgressWithStartAndTick', function(action, start, tick, finish)
210 | StartProgress(action, start, tick, finish)
211 | end)
212 |
213 | RegisterNetEvent('progressbar:client:cancel', function()
214 | ActionCleanup()
215 | SendNUIMessage({
216 | action = 'cancel'
217 | })
218 | end)
219 |
220 | -- NUI Callback
221 |
222 | RegisterNUICallback('FinishAction', function(data, cb)
223 | ActionCleanup()
224 | cb('ok')
225 | end)
226 |
227 | -- Exports
228 |
229 | local function Progress(action, finish)
230 | StartProgress(action, nil, nil, finish)
231 | end
232 | exports('Progress', Progress)
233 |
234 | local function ProgressWithStartEvent(action, start, finish)
235 | StartProgress(action, start, nil, finish)
236 | end
237 | exports('ProgressWithStartEvent', ProgressWithStartEvent)
238 |
239 | local function ProgressWithTickEvent(action, tick, finish)
240 | StartProgress(action, nil, tick, finish)
241 | end
242 | exports('ProgressWithTickEvent', ProgressWithTickEvent)
243 |
244 | local function ProgressWithStartAndTick(action, start, tick, finish)
245 | StartProgress(action, start, tick, finish)
246 | end
247 | exports('ProgressWithStartAndTick', ProgressWithStartAndTick)
248 |
249 | local function isDoingSomething()
250 | return isDoingAction
251 | end
252 | exports('isDoingSomething', isDoingSomething)
253 |
--------------------------------------------------------------------------------
/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | fx_version 'cerulean'
2 | lua54 'yes'
3 | game 'gta5'
4 |
5 | author 'qbcore-framework'
6 | description 'Dependency for creating progressbars in QB-Core.'
7 | version '1.0.0'
8 |
9 | ui_page 'html/index.html'
10 |
11 | client_script 'client.lua'
12 |
13 | files {
14 | 'html/index.html',
15 | 'html/style.css',
16 | 'html/script.js'
17 | }
18 |
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |