Progress Bars

A common question is often regarding the progress bars. Can I change the progress bar? Is the progress bar open source? Yes & yes! However, it requires a bit of technical know-how. We will explain how the progress bar code works, then provide an example of how to change it below!

How Progress Bars Work

By default, the progress bars included are from the Overextended library, also known as ox_lib. You can learn more about the ox_lib progress bars here. These progress bars are synchronous, meaning it executes one request at a time and waits for that request to be answered. Because of that, we can use a simple function to handle the progress bar, which is:

-- Function to handle progress bar
--- @param data table Config.Animations.X
ProgressBar = function(data)
    if lib[progressType](data) then
        return true
    end
    return false
end

In short, if the progress bar completes, it returns true and if it is not completed it returns false. Thus telling whatever block of code is executing it to continue a certain action or not. You'll notice the parameter, "data" in the function above with the comment telling you it's coming from the table Config.Animations.X - the "X" representing any one of the options from that table which could be fertilizing, harvesting, destroying and so on. For example:

harvesting = { -- Animation(s) used when harvesting a plant
    label = 'Harvesting..',
    duration = 4000,
    position = 'bottom',
    useWhileDead = false,
    canCancel = false,
    disable = { move = true, car = true, combat = true },
    anim = { dict = 'amb@prop_human_bum_bin@base', clip = 'base' },
    prop = { }
},

When we put this all together, this is what the function will look like when it's executing the harvest plant event:

ProgressBar = function(data)
    if lib.progressCircle({ -- Animation(s) used when harvesting a plant
        label = 'Harvesting..',
        duration = 4000,
        position = 'bottom',
        useWhileDead = false,
        canCancel = false,
        disable = { move = true, car = true, combat = true },
        anim = { dict = 'amb@prop_human_bum_bin@base', clip = 'base' },
        prop = { }
}) then
        return true
    end
    return false
end

In the end, it's simply just executing the ox_lib progressBar or progressCircle depending on your Config.ProgressType selection. This is how all our animations are handled in the resource and every bit of data relating to the animations and progress bar code is thus open source and available to you.

If you are looking to change the ProgressBar function to use a different progress bar (such as QBCore's or something else) it must support the ability to handle animations! If the progress bar you wish to use does not support animations, then it will not work.

Change to QB Progress Bar

Part 1

As we learned above, all the code relating to progress bars is open source, which means you can edit or change this however you wish! The hardest part is possessing the knowledge to do so. Since QBCore's progress bars support the ability to use animations, we can indeed make this change! We'll provide an example below of how to change this function to use QBCore's progress bars instead of ox_lib's.

To get started, we need to navigate to lation_weed/client/functions.lua and find our ProgressBar function, seen below:

-- Function to handle progress bar
--- @param data table Config.Animations.X
ProgressBar = function(data)
    if lib[progressType](data) then
        return true
    end
    return false
end

We need to replace that function with this updated version to use QBCore:

-- Function to handle progress bar
--- @param data table Config.Animations.X
ProgressBar = function(data)
    local complete = false
    QBCore.Functions.Progressbar(data.name, data.label, data.duration,
        data.useWhileDead, data.canCancel, data.disable,
        data.animation, data.prop, data.propTwo,
        function() -- Progress bar completed
            complete = true
        end,
        function() -- Progress bar did not complete
            complete = false
        end)
    Wait(data.duration + 250)
    return complete
end

Let's explain our changes here. First, you'll notice we are now explicitly assigning the data variables to the locations they belong in to ensure the progress bar data is being passed correctly. Second, you'll notice the addition of a new variable: complete. This is used to track the success of the progress bar completion. As we noted above, ox_lib's progress bars are synchronous, but QBCore's progress bars are asynchronous. Essentially meaning, the value of "complete" will be returned before the progress bar is even completed. If we don't halt the code while we await for the proper value, it will always return false, thus stopping whatever action we were attempting to complete. Therefore you'll notice our second addition here towards the bottom: "Wait(data.duration + 250)" - this is simply stopping the code from executing until the progress bar is completed and the value of "complete" is properly updated to true or false.

Part 2

Our next step is now to update our Config.Animations table to use QBCore's format instead of ox_lib, since all the data passed to this function is coming directly from there. In this example, we'll update the plantingPot animation - the animation played and progress bar displayed immediately after using a seed to place a plant somewhere. In the Config.Animations table, it is found right at the top and looks like this:

placingPot = { -- Animation(s) used during initial start of plant placement
    part1 = { -- Played when player first "uses" a seed
        label = 'Placing pot..',
        duration = 1200,
        position = 'bottom',
        useWhileDead = false,
        canCancel = true,
        disable = { move = true, car = true, combat = true },
        anim = { dict = 'pickup_object', clip = 'pickup_low' },
        prop = { }
    },
    part2 = { -- Played after part1, and continues until confirmation/cancellation of placement
        anim = { dict = 'missheist_jewelleadinout', clip = 'jh_int_outro_loop_a', flag = 51 }
    }
},

We only need to worry about "part1" - since it tells us this is played when the player first uses a seed. We need to replace all this data (only the part1) with data that QBCore's progress bars accept. We can highlight and replace the entire part1 section with this version:

part1 = { -- Played when player first "uses" a seed
    name = 'placingpot',
    duration = 1200,
    label = 'Placing pot..',
    useWhileDead = false,
    canCancel = true,
    disarm = true,
    disable = { disableMovement = true, disableCarMovement = true, disableMouse = false, disableCombat = true },
    animation = { animDict = 'pickup_object', anim = 'pickup_low' },
    prop = { },
},

You'll quickly notice it all looks very similar! However, the name of certain sections are slightly different. Also, there is the addition of a new field, "name" at the top - which QBCore requires. This can be anything, but at least make it unique and ensure no special characters. In this example we simply call it "placingpot". Another new field is the "disarm" field, which we set to true but this can be set however you desire. Other than that, it's simply a matter of differently named variables because we are using a different progress bar function!

Now, we can save both our functions.lua file and our config.lua file, restart the resource & try placing a pot! You'll notice now that the resource uses QBCore's progress bar and no longer uses ox_lib's! You would repeat this process (part 2 only) with all other animations left in Config.Animations, using the above as an example. Enjoy!

Last updated