• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

ProgressBars v2.0.1

Now supports multiple progress bar models (via dummies).

JASS:
library ProgressBars requires TimerUtils optional BoundSentinel
/**************************************************************
*
*   ProgressBars v2.0.1 by TriggerHappy
*
*   This library allows you to easily create and modify progress bars.
*   It works by creating a dummy unit with a special model and changing
*   the animation speed to increase or reduce the bar speed. It is more than
*   just a wrapper as it recycles each progress bar, meaning it will avoid
*   costly CreateUnit calls whenever possible which also leak.
*
*   Options:
*       x            - set X coordinate
*       y            - set Y coordinate
*       xOffset      - offset of the target unit, if any.
*       yOffset      - offset of the target unit, if any.
*       zOffset      - how high the bar is from the ground.
*       color        - allows you to tint the bar or add transparency
*       targetUnit   - pick which unit the bar should hover over
*       size         - set model scale
*
*   Usage:
*       local ProgressBar bar = ProgressBar.create()
*       set bar.zOffset       = 150
*       set bar.color         = PLAYER_COLOR_RED
*       set bar.targetUnit    = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
*       call bar.setPercentage(30)
*
*   Installation:
*       1. Copy the dummy unit over to your map
*       2. Change the DUMMY constant to fit the Raw code of the dummy.
*       3. Copy this and all required libraries over to your map.
*
*   Thanks to JesusHipster for the Progress Bar models
*   and to Vexorian for TimerUtils & BoundSentinel
*
**************************************************************/

    globals
        private constant integer PROGRESS_BAR_DUMMY     = 'pbar' // the default one
        private constant player  PROGRESS_BAR_OWNER     = Player(PLAYER_NEUTRAL_PASSIVE) // owner of the dummy
        private constant real    UPDATE_POSITION_PERIOD = 0.03 // the timer period used with .targetUnit
    endglobals
    
    struct ProgressBar
    
        unit bar
        unit target
        
        real xOffset = 0
        real yOffset = 0
        
        timer timer
        timer timer2
        
        private boolean t_enabled = false
        private real endVal
        private real curVal=0
        private real pspeed=0
        private boolean reverse
        private boolean done
        private boolean recycle
        
        readonly static unit array dummy
        readonly static integer lastDummyIndex = -1

        method operator x= takes real x returns nothing
            call SetUnitX(this.bar, x)
        endmethod
        
        method operator x takes nothing returns real
            return GetUnitX(this.bar)
        endmethod
        
        method operator y= takes real y returns nothing
            call SetUnitY(this.bar, y)
        endmethod
        
        method operator y takes nothing returns real
            return GetUnitY(this.bar)
        endmethod
        
        method operator zOffset= takes real offset returns nothing
            call SetUnitFlyHeight(this.bar, offset, 0)
        endmethod
        
        method operator zOffset takes nothing returns real
            return GetUnitFlyHeight(this.bar)
        endmethod
        
        method operator size= takes real size returns nothing
            call SetUnitScale(this.bar, size, size, size)
        endmethod
        
        method operator color= takes playercolor color returns nothing
            call SetUnitColor(this.bar, color)
        endmethod
        
        method show takes boolean flag returns nothing
            call UnitRemoveAbility(this.bar, 'Aloc')
            call ShowUnit(this.bar, flag)
            call UnitAddAbility(this.bar, 'Aloc')
        endmethod
        
        method reset takes nothing returns nothing
            call SetUnitAnimationByIndex(this.bar, 1)
        endmethod

        method RGB takes integer red, integer green, integer blue, integer alpha returns nothing
            call SetUnitVertexColor(this.bar, red, green, blue, alpha)
        endmethod
        
        method destroy takes nothing returns nothing
            if (recycle) then
                set lastDummyIndex = lastDummyIndex + 1
                set dummy[lastDummyIndex] = this.bar
                call SetUnitAnimationByIndex(this.bar, 0)
                call SetUnitTimeScale(this.bar, 1)
            endif
            
            set this.bar        = null
            set this.target     = null
            set this.t_enabled  = false
            set this.endVal     = 0
            set this.curVal     = 0
            
            if (this.timer != null) then
                call ReleaseTimer(this.timer)
                set this.timer = null
            endif
            
            if (this.timer2 != null) then
                call ReleaseTimer(this.timer2)
                set this.timer2 = null
            endif
        endmethod
        
        private static method updatePercentage takes nothing returns nothing
            local timer expired = GetExpiredTimer()
            local thistype this = GetTimerData(expired)
            
            if (this.reverse) then
            
                if (this.curVal > this.endVal) then
                    call SetUnitTimeScale(this.bar, -this.pspeed)
                    set this.curVal = (this.curVal - (this.pspeed))
                elseif (this.curVal <= this.endVal) then
                    call PauseTimer(this.timer2)
                    call SetUnitTimeScale(this.bar, 0)
                    set this.curVal = this.endVal
                    set this.done   = true
                endif
                
            else
            
                if (this.curVal < this.endVal) then
                    call SetUnitTimeScale(this.bar, this.pspeed)
                    set this.curVal = (this.curVal + (this.pspeed))
                elseif (this.curVal >= this.endVal) then
                    call PauseTimer(this.timer2)
                    call SetUnitTimeScale(this.bar, 0)
                    set this.curVal = this.endVal
                    set this.done   = true
                    
                endif
                
            endif
            
        endmethod
        
        private static method updatePosition takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            if (this.target != null) then
                call SetUnitX(this.bar, GetUnitX(this.target) + xOffset)
                call SetUnitY(this.bar, GetUnitY(this.target) + yOffset)
            else
                call ReleaseTimer(GetExpiredTimer())
            endif
        endmethod
        
        private static method getDummy takes nothing returns unit
            if (lastDummyIndex <= -1) then
                set bj_lastCreatedUnit = CreateUnit(PROGRESS_BAR_OWNER, PROGRESS_BAR_DUMMY, 0, 0, 270)
                call PauseUnit(bj_lastCreatedUnit, true)
                return bj_lastCreatedUnit
            endif
            call SetUnitAnimationByIndex(dummy[lastDummyIndex], 1)
            set lastDummyIndex = lastDummyIndex - 1
            return dummy[lastDummyIndex + 1]
        endmethod
        
        static method release takes integer count returns nothing
            if (count > thistype.lastDummyIndex) then
                set count = thistype.lastDummyIndex
            endif
                
            loop
                exitwhen count <= 0
                call RemoveUnit(dummy[count])
                set dummy[count] = null
                set count = count - 1
            endloop
                
            set thistype.lastDummyIndex = -1
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            
            set this.bar        = thistype.getDummy()
            set this.done       = true
            set this.recycle    = true
            
            call SetUnitAnimationByIndex(this.bar, 1)
            call SetUnitTimeScale(this.bar, 0)
            
            return this
        endmethod
        
        static method createEx takes integer unitId returns thistype
            local thistype this = thistype.allocate()
            
            set this.bar        = CreateUnit(PROGRESS_BAR_OWNER, unitId, 0, 0, 0)
            set this.done       = true
            set this.recycle    = false
            
            call SetUnitAnimationByIndex(this.bar, 1)
            call SetUnitTimeScale(this.bar, 0)
            
            return this
        endmethod
        
        method setPercentage takes real percent, real speed returns nothing
            set this.endVal = R2I(percent)
            set this.pspeed = speed
            
            set this.reverse = (curVal > endVal)
                
            if (this.done) then
                
                if (this.timer2 == null) then
                    set this.timer2 = NewTimerEx(this)
                endif
            
                call TimerStart(this.timer2, 0.01, true, function thistype.updatePercentage)
                set this.done=false
            endif
        endmethod
        
        method operator targetUnit= takes unit u returns nothing
            set this.target = u
            
            if (u != null) then
                if (this.timer == null) then
                    set this.timer = NewTimerEx(this)
                endif
                call TimerStart(this.timer, UPDATE_POSITION_PERIOD, true, function thistype.updatePosition)
                call SetUnitX(this.bar, GetUnitX(this.target) - xOffset)
                call SetUnitY(this.bar, GetUnitY(this.target) - yOffset)
                set this.t_enabled = true
            else
                if (this.timer != null) then
                    call ReleaseTimer(this.timer)
                endif
                set this.t_enabled = false
            endif
        endmethod
        
    endstruct
    
endlibrary

Changelog

Code:
v2.0.1
- Added support for different types of progress bars (without recycling) via .createEx(integer unitId).
- Properly nulled the timer handles.
- Paused progress bar dummies to reduce CPU usage.

v2.0.0
- Default health bar hidden in demo.
- ProgressBars will now no longer fill up out of nowhere.
- Fixed a possible, rare leak associated with not nulling a timer.

v2.0.0 Beta
- beginProgression and everything associated with it has been removed. Use .setPercentage instead.
- zOffset getter included.
- xOffset and yOffset added.
- show method now adds and removes locust to prevent it removing.

v1.0.2
- Minor efficiency improvements
- freeDummies member renamed to lastDummyIndex

v1.0.1
- Fixed a bug where the bar would fade out really slow on long durations.
- Removed unused parameters from the getDummy method.
- Removed UnitAddAbility and gave the dummy the ability directly.
- targetUnit operator now sets the position of the bar directly (fixes cosmetic bug)
- Changed onDestroy to destroy.

Thanks to JesusHipster for the Progress Bar V2 model.


Keywords:
progress,bar,xp,exp,mana,hp,health,loot,mui,hp,mp,cast,bars
Contents

ProgressBars v2.0.1 (Map)

Reviews
18th Apr 2016 Your resource has been reviewed by BPower. In case of any questions or for reconfirming the moderator's rating, please make use of the Quick Reply function of this thread. Review: A wonderful system and definitly worth a 5/5...
Level 3
Joined
Jun 13, 2010
Messages
43
Yeah the documentation clearly includes all of that, except for GUI support which would be ridiculous to include in the doc.

JASS:
*   Options:
*       x            - set X coordinate
*       y            - set Y coordinate
*       xOffset      - offset of the target unit, if any.
*       yOffset      - offset of the target unit, if any.
*       zOffset      - how high the bar is from the ground.
*       color        - allows you to tint the bar or add transparency
*       targetUnit   - pick which unit the bar should hover over
*       size         - set model scale
or more specifically, you were complaining about not being able to set the height of the model

JASS:
local ProgressBar bar = ProgressBar.create()
set bar.zOffset = 100
I do agree that more samples could be included, but this is very straight forward to use and the API is simplistic.

More than enough information is embedded into the current examples.

Please stop putting negativity out there about my system when it's not even true.

Well i said its a great system didn't i? Don't get too defensive, All i trying point out was just adding more sample documentation in terms to the level for even GUI users or new jass users able to understand it . That would make this system even more user friendly. It would be such a waste if newbie can't use it. Isn't it :)?

Anyways i am not sure about the lag most ppl talking about in this thread. But the ideal having 2 dummy locking to the position of every each unit in the map isn't ideal for most people. It still takes memory in wc3. well doesn't matter tho. No Negativity intended , No kitten is harmed during this process.
 
Well i said its a great system didn't i? Don't get too defensive, All i trying point out was just adding more sample documentation in terms to the level for even GUI users or new jass users able to understand it . That would make this system even more user friendly. It would be such a waste if newbie can't use it. Isn't it :)?

Anyways i am not sure about the lag most ppl talking about in this thread. But the ideal having 2 dummy locking to the position of every each unit in the map isn't ideal for most people. It still takes memory in wc3. well doesn't matter tho. No Negativity intended , No kitten is harmed during this process.

Yeah but you also said there's no way it could support 30+ units, that you can't change the height of the dummy, and that it wasn't documented for GUI (when it's a vJass system). I wasn't taking it offensively, but you were putting out false negativity.

That said, I appreciate the support and I'm glad you like and I will put more samples and fix it up the documentation more next version.
 
Level 3
Joined
Dec 8, 2013
Messages
51
Often hp and mp bar's do not work correctly,and when the death of a unit sometimes refuse to disappear.
Why is approved?
 

Deleted member 219079

D

Deleted member 219079

My map crashes on set bar = ProgressBar.createEx('pbar')

Edit: I caused infinite loop because of giving dummies healthbars..

Edit2: The system is still bugged; well I fixed that myself, but then again there's this bug where the health bars don't show the accurate number, for example when they get created and their percentage is set to 100, it doesn't show a value of 100, instead it shows like 80~90.
 
Last edited by a moderator:

Deleted member 219079

D

Deleted member 219079

I'm seriously starting to suspect this doesn't work at all :(
 
Level 13
Joined
May 24, 2005
Messages
609
I think the documentation is lacking information on how to properly destroy/release finished bars; what shall I do? Call destroy method, release method, or both?
The release method has an argument, but it's not explained anywhere, nor is the method used internally within the library as it seems.

I guess the system's approach is meant to be used as having permanent bars attached to units, so that they are displayed when used or spells are cast and such. I see that you wanted to avoid CreateUnit calls. My approach/need for using this is more like creating bars when necessary, use them once, and then release/destroy them afterwards. I feel that going this way can be a bit tricky with this system. Currently, each time a bar is created, a dummy unit is created as well. However, calling the destroy() method won't remove the dummy unit. Hmm.. maybe I need to have a second look at the code though.

EDIT: Just 2 more things I've noticed:
- The destroy method seems to be missing something like this.deallocate()
- In the updatePercentage method, the timer variable expired isn't nullified

EDIT2: While trying to set certain percentages at (nearly) instant speed (using high speed values), I think I might have experienced some bugs when using particular/high speed with setPercentage, but I need to do some further testing.

Besides, nice and useful system.
 
Last edited:
Level 3
Joined
Dec 8, 2013
Messages
51
I like the systems on hive that are not normally explaining how to use it, like an author also doesnt know. But it's a great argument to give 5 grade for the system...
 
Level 8
Joined
Jul 10, 2008
Messages
353
JASS:
Usage:
*       local ProgressBar bar = ProgressBar.create()
*       set bar.zOffset       = 150
*       set bar.color         = PLAYER_COLOR_RED
*       set bar.targetUnit    = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
*       call bar.setPercentage(30)
Are those the wrappers? This really really needs better documentation.

Also the jass functions are obfuscated af. Don't know how they look like in vjass but this
JASS:
function s__ProgressBar__get_zOffset takes integer this returns real
            return GetUnitFlyHeight(s__ProgressBar_bar[this])
endfunction
Is not easy to remember while writing code down...
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
they are obfuscated because they are meant to be used from vJass, not from plain Jass, because when you compile the vJass code it has to put all the crap into the name of the function to make sure it wont collide with others(you can have two private functions with the same name in two different scopes in vJass, it has to be disambiguated by appending crap like the scope name and shit to the name of the function)
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Are the hp bar supposed to remain after the unit died? They look very much not alive to me.

upload_2018-7-31_13-43-25.png


A decent update would be if you could determine which players should be allowed to see the hp bar (local, allied, enemey, all)

Also how did you disable the regular hp/mana bar tha wc3 provides, anyway to enable them?

Does each instance have two timers, one for position and one for percentage why not have them run on the same. Or am I missing something?
 
Last edited:
Top