1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The poll for our 11th Music Contest is up! Help us choose the most awesome cinematic tracks by casting a vote!
    Dismiss Notice
  3. Melee Mapping contest #3 - Poll is up! Vote for the best 4v4 melee maps!
    Dismiss Notice
  4. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  5. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice

ProgressBars v2.0.1

Submitted by TriggerHappy
This bundle is marked as approved. It works and satisfies the submission rules.
Now supports multiple progress bar models (via dummies).

Code (vJASS):

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 (Text):

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
Moderator
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...
  1. 18th Apr 2016

    General Info

    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 rating.
    Recycling bar dummies is a very welcome bonus.

    Why is BoundSentinel in the demo map?

    Troubleshooting:

    • The hardcoded timer timeout in method setPercentage could be replaced by a descriptive constant.
      ---
    • Each time you release a timer you should null that timer handle, because of code blocks like:
      Code (vJASS):
      if (this.timer == null) then
          set this.timer = NewTimerEx(this)
      endif

      It implies that the released timer is still reserved for your system, which is not true.
      TimerUtils probably gave the timer out for another code running.
      It's very likely because the timer stack always returns the last recycled timer.
      ---
    • if (this.target != null) then
      could preferably be
      if GetUnitTypeId(target) != 0 then

    Review changelog:
    1. -
     
  2. Rheiko

    Rheiko

    Joined:
    Aug 27, 2013
    Messages:
    2,942
    Resources:
    7
    Icons:
    2
    Spells:
    3
    Tutorials:
    2
    Resources:
    7
    cool, but what is progress bar for?
    something like exp/hp/mana bar?
     
  3. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,431
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    cast bar for spells I guess
     
  4. Rheiko

    Rheiko

    Joined:
    Aug 27, 2013
    Messages:
    2,942
    Resources:
    7
    Icons:
    2
    Spells:
    3
    Tutorials:
    2
    Resources:
    7
    that's the first thing that comes to my mind..
     
  5. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    It can be used for anything you want.
     
  6. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    The progress bar model is just really nice :).

    The dummy needs locust.

    version 0.01 ?? rather 1.0.0

    Edit:
    timer timer
    Why ....

    private static method getDummy takes real x, real y returns unit
    You don't use x and y at all.

    I thought method onDestroy should be avoided. Can't argue about this, its just something I read, but I think you just should write method destroy on your own.

    Also in onDestroy you do
    call SetUnitAnimationByIndex(this.bar, 0)
    which looks ugly in your demomap, in case you cancel the spell.
    I would recommend using
    call SetUnitPosition(this.bar, 2147483647, 2147483647)
    before. ( or any really high x/y value)

    You could use 0.031250000 instead of 0.03, it would be more precise.

    I would create new dummies at the edge of the map, just use SetUnitPosition and a hillarious high x and y value, because if it is on 0,0 it will be displayed there or at its current position for a millisecond before it gets moved above the targetunit. (You can experience it in your demo map)
    When recycling the dummy place him back to the edge of the map.

    You could mention your demo spell leaks and should not be copied. ^^

    If it gets approved, I'll use it as optional library for my TownPortalSystem.
     
    Last edited: Dec 14, 2013
  7. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,431
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    I think you could do this pretty simple by just having a model with an 1 second duration animation. Then set the animation to speed to lets say 50% as a cast bar. I think there is a system that works that way,
     
  8. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    No you can't.
     
  9. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,532
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    Going off of the above post.

    This should not have been submitted unless it is verion 1.0.0 or higher.

    timer timer should be avoided.

    onDestroy should never be used create your own destroy method.
    Look at Magtheridon's Struct for Dummies tutorial i believe the exact reason is in there. Also don't use SetUnitPosition. It is slower and less efficient than the SetUnitX/Y counterparts.

    0.031250000 should be used. It is used by almost all jassers / vJassers

    SetUnitX/Y with a hide unit and move then show unit will fix this problem.
     
  10. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    SetUnitX/Y is no option, because you don't know how large the map is. You could easily move the unit outside mapbounds and crash the game. That's why you have to use SetUnitPosition.

    Nestharus did it in his Dummy library and I think its a good solution.
     
  11. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Because
    this.timer
    looks nice (in TESH), leave me alone.

    Thanks for pointing that out, I left that there on accident.

    onDestroy is fine.

    I personally use 0.01, I only keep it at 0.03 to get approved here.

    What's wrong with the death animation? How does that look ugly. The only reason it looks ugly (has nothing to do with the line you pointed out) when you cancel the spell is because it doesn't follow the unit, but I'll work on that.

    Jesus christ, the version number is really just cosmetic. The system works fine you're really saying I shouldn't submit this because I titled it 0.01? Get it together lol.

    Anyway changed version number to 1.00 (will update doc and map in next release) and uploaded a video.

    I refuse to use SetUnitPosition, or even path check for that matter (in this lightweight system). I will consider bound sentinel as an optional dependency.
     
    Last edited: Dec 14, 2013
  12. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,532
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    It is in the rules for the spell submission section. Do not submit anything under version 1.0

    Use the world bounds library to detect when unit is leaving map area.
     
  13. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,167
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    First off: I like the idea of progress bars like this in game and like that there is finally a library for it.

    My questions/ideas:
    Why did you add + 0.1 to the time? Isn't 0.01 enough?
    You should change the name of the timer to clock or something more appropriate.
    You can add optional ARGB support. (Vexorians Version)
    Why is t_enabled on default visibility? Shouldn't it be private?
    Last but not least, the demo really sucks. Make it a spell with start delay, one over time (rain of fire / blizzard) and maybe some hp or mana bar for another unit.

    Edit: The show method is funny :D

    You can change this
    Code (vJASS):

            method show takes boolean flag returns nothing
                if (flag == false) then
                    set this.hidden = true
                    call ShowUnit(this.bar, false)
                elseif (hidden) then
                    set this.hidden = false
                    call ShowUnit(this.bar, true)
                endif
            endmethod
     


    to this

    Code (vJASS):

            method show takes boolean flag returns nothing
                set this.hidden = not flag
                call ShowUnit(this.bar, flag)
            endmethod
     


    It's okay if it's already shown, it won't flicker or anything.
     
  14. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Why would I constantly check for safe coordinates (every 0.03 seconds) when I could just use bound sentinel?

    Nice catch, thanks.

    That's what coding at 2am gets you;)

    Thanks.

    I'll be updating in a little, right now the progression formula is a bit wrong.

    Updated, however there is still an issue with the AnimationSpeed formula in that over long a duration it becomes slightly more inaccurate.

    Also please nobody complain about the bar not being destroyed when the demo spell is interrupted, I'll have the example working in the next release.
     
    Last edited: Dec 14, 2013
  15. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    if (this.t_enabled == false) then

    ->
    if not this.t_enabled then


    Anyway that looks good I'll review better tomorrow if I got some time :)
    +rep anyway !

    EDIT : Need to spread :/
     
  16. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,167
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    I would make these private:

    Code (vJASS):

            boolean t_enabled = false
           
            real finishTime = -1
            real ticks = 0
     


    And make a method operator (Getter) as "elapsed / remaining".
     
  17. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    The not operator is slower than ==.

    True
     
  18. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    The not is slower ?
    Seriously ?
    Thanks for the info :)
     
  19. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,538
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Actually, I guess that was a myth.

    I'll update the benchmarking thread and this accordingly.