1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Head to the 33rd Modeling Contest Poll and drink to your heart's desire.
    Dismiss Notice
  3. Choose your means of doom in the 17th Mini Mapping Contest Poll.
    Dismiss Notice
  4. A slave to two rhythms, the 22nd Terraining Contest is here.
    Dismiss Notice
  5. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  6. The die is cast - the 6th Melee Mapping Contest results have been announced. Onward to the Hive Cup!
    Dismiss Notice
  7. The glory of the 20th Icon Contest is yours for the taking!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

TimedUnitScale v2.0

Submitted by IcemanBo
This bundle is marked as approved. It works and satisfies the submission rules.
Info

Allows you smoothly to change a unit's scale over time.

Code
Code (vJASS):

library TimedUnitScale /* v2.0

    Information
    ¯¯¯¯¯¯¯¯¯¯¯

        Allows you smoothly to change a unit's scale over time.

        It is required you to read the GetUnitScale library,
        if you modified default scale values in object editor.
       
    Credits:
    ¯¯¯¯¯¯¯¯
       
        Resources:
       
            - TriggerHappy
            - Nestharus


 */
requires /*

        */
GetUnitScale   /* hiveworkshop.com/forums/submissions-414/snippet-getunitscale-262274/
        */
UnitDex        /* hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
        */
List           /* attached in map and in thread, because nestharus removed it I can't find original destinaion anymore

       
       
    struct UnitScale
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
   
   
        static method operator enableAll= takes boolean flag returns nothing
            - when set to "false" it will pause all scale changes for all units
            - when set to "true" it will continue again
   
        static method operator [] takes unit who returns thistype
            - That's the only interface for the user to get an instance.
            - Use this to call methods or to set/read other members:
           
           
            method apply takes real newScale, real duration returns UnitScaleData
                - use this to apply a new scale change
                - you also can use GetUnitScale function to mimic add & substract behaviour.
           
            method stopScale takes UnitScaleData sd returns nothing
                - you can use the return value from the "apply" method to manualy destroy a scale instance.
                - do this at your own risk
               
            method destroy
                - "destroy" will destroy all scale instances.
               
               
            boolean enabled
                - set this to "false" if you want to disable, but not destroy a unit's scale changes
                - when set to "true" it will continue again
               
            readonly boolean exists
                returns the flag if there currently exists any scale instance
               
            readonly integer count
                returns amount of current scale instances for our unit
               
               
        Example:
            local UnitScale myInstance = UnitScale[GetTriggerUnit()]
                - we get the instance for our unit.
           
            local UnitScaleData data = myInstance.apply(5, 10)
                - this will call the apply method for myInstance.
                - it will try to set it's scale to "5" over "10" seconds.
               
            call instance.stopScale(data)
                - will destroy the applied scale instance
           
           
        Pay attention for the example. When the user will start multiple scale instances,
        it oboviously is not ensured that the unit will have exactly the scale of "5" after "10" seconds.
        If you want ensure accurate values you can check for already running instances, or use the .destroy() method before apply().
       
   
    struct UnitScaleData
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
   
        That's the struct returned by the apply method above.
        You have further access from it:
       
            method operator time takes nothing returns real
                - returns the remaining time of the instance
               
            method operator time= takes real r returns nothing
                - define a new remaining time
                - only positives will be allowed
               
            method operator scalePerSecond takes nothing returns real
                - return the current scale rate perSecond
           
            method operator scalePerSecond= takes real r returns nothing
                - define a new sclare rate perSecond.
                - only positives will be allowed.
       
       
**************************************************************************/


    globals
        private constant timer CLOCK    = CreateTimer()
        private constant real  TIMEOUT  = .031250000
    endglobals

    struct UnitScaleData extends array
        implement List
        real duration     // Remaining duration for scaling
        real amount       // scale-amount per interval
       
        method operator time takes nothing returns real
            return .duration
        endmethod
        method operator time= takes real r returns nothing
            set .duration = RAbsBJ(r)
        endmethod
       
        method operator scalePerSecond takes nothing returns real
            return .amount/TIMEOUT
        endmethod
        method operator scalePerSecond= takes real r returns nothing
            set .amount = RAbsBJ(r)*TIMEOUT
        endmethod
    endstruct
   
    struct UnitScale extends array
        private static boolean systemEnabled   = false
       
        boolean enabled
        readonly boolean exists     // Does a UnitScale currently exist?
        readonly integer count      // How often a unit is currently regstered.
       
        private unit u
        private UnitScaleData head
        private thistype prev
        private thistype next
        private static thistype first = 0
       
        static method operator [] takes unit who returns thistype
            return GetUnitId(who)
        endmethod
       
        private method destroy takes nothing returns nothing
       
            if not .exists then
                return
            endif
           
            call .head.destroy()
            set .count = 0
           
            if (this == thistype.first) then
                set thistype.first = this.next
            endif
            set this.next.prev = this.prev
            set this.prev.next = this.next
               
            if (thistype.first == 0) then
                call PauseTimer(CLOCK)
                set thistype.systemEnabled = false
            endif
            set .exists = false
            set .u = null
        endmethod
       
        method stopScale takes UnitScaleData sd returns nothing
            set .count = .count - 1
            if .count == 0 then
                call.destroy()
            else
                call sd.remove()
            endif
        endmethod
   
        private static method callback takes nothing returns nothing
            local thistype this = thistype.first
            local UnitScaleData element
            local UnitScaleData temp
            loop
                exitwhen (this == 0)
               
                if (GetUnitTypeId(.u) == 0) then
                    call .destroy()
               
                elseif (.enabled) then
                   
                    set element = .head.first
                    loop
                        exitwhen element == 0
                        set temp = element.next
                       
                        call SetUnitScale(.u, (GetUnitScale(.u) + element.amount), 1, 1)
                        if (element.duration <= TIMEOUT) then
                            call .stopScale(element)
                        else
                            set element.duration = element.duration - TIMEOUT
                        endif
                       
                        set element = temp
                    endloop
                   
                endif
                set this = this.next
            endloop
        endmethod
       
        method apply takes real newScale, real duration returns UnitScaleData
            local unit u = GetUnitById(this)
            local UnitScaleData element
           
            if (newScale == GetUnitScale(u)) then
                return 0
            endif
           
            if (duration < 0) then
                debug call BJDebugMsg("UnitScale: Durations smaller or equal 0 will lead to instant scaling.")
                set duration = 0
            endif
           
            if (duration == 0) then
                call SetUnitScale(u, newScale, 1, 1)
                return 0
            endif
           
            set this = GetUnitId(u)
            if .count == 0 then
                set .head = UnitScaleData.create()
                set .enabled = true
                set .exists = true
                set .u = u
               
                if (thistype.first == 0) then
                    call TimerStart(CLOCK, TIMEOUT, true, function thistype.callback)
                    set thistype.systemEnabled = true
                endif
                set this.next = thistype.first
                set thistype.first.prev = this
                set thistype.first = this
                set this.prev = 0
            endif
           
            set element = .head.enqueue()
            set element.duration = duration
            set element.amount   = (newScale - GetUnitScale(u)) / (duration/TIMEOUT)
            set .count = .count + 1
           
            return element
        endmethod
       
        static method operator enableAll= takes boolean flag returns nothing
            if flag and not thistype.systemEnabled and thistype.first != 0 then
                call TimerStart(CLOCK, TIMEOUT, true, function thistype.callback)
                set thistype.systemEnabled = flag
            elseif not flag and not thistype.systemEnabled then
                call PauseTimer(CLOCK)
                set thistype.systemEnabled = flag
            endif
        endmethod
    endstruct
endlibrary
 


Changelog

v2.0
- Internaly the structure is a bit cleaner and uses an other struct.
- Reworked API syntax and added a bit more powers for user.​
v.1.6a
- Removed not directly needed requierements.
- Moved timer callback, to prevent trigger evaluation.​
v.1.6
- Updated GetUnitScale in demo.​
v.1.5.0a
- Removed stop-scale-onDeath config.​
v.1.5.0
- Seperated GetUnitScale from system.
- Only allocate if all conditions are true.​
v.1.4.2
- Renamed two API functions. Again :eek:...​
v1.4.1
- Forgot to rename 2 API functions. :D
v1.4.0
- Structure reduced. Double scaling is not possible anymore. Only single scale -> straight forward.​
v1.3.2
- Static if added for
onDeath
.​
v1.3.1
- Optimized method
onDeindex
.​
v1.3.0
- Cancel scaling onDeindex.
- Canceling scaling onDeath now possible.​
v1.2.1
- Note was outdated. Changed.
- API functions moved below struct.​
v1.2.0
- Now also 0 and negative values are allowed as parameter, and will be automatically be fixed, if needed.
- Fixed a bug with scale accuracy.​
v1.1.0
-
function GetUnitDefaultScale
added.
-
function StopGrows
added.
-
function ResetScale
added.
- Division with 0 bug fixed.
- Tiny code optimizations.​
v1.0
- release​

Keywords:
Timed, Unit, Scale, System, Grow, Shrink, Effect, IcemanBo, null
Contents

TimedUnitScale (Map)

Reviews
Moderator
11:23, 22th May 2015 BPower: Looks good to me. Approved. Please remove not directly needed requirements. 22:22, 8th Jan 2015 Orcnet: Set to Need Fix, for now
  1. 11:23, 22th May 2015
    BPower: Looks good to me. Approved.
    Please remove not directly needed requirements.

    22:22, 8th Jan 2015
    Orcnet: Set to Need Fix, for now
     
  2. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,532
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Actually, I'm not sure about how Hive handles Nes's resources atm. Tell me if it's a problem. :d
     
  3. GywGod133

    GywGod133

    Joined:
    Jul 16, 2012
    Messages:
    630
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    This is better for Tiny's Grow

    EDIT:
    Not bad...
     
  4. Kazeon

    Kazeon

    Joined:
    Oct 12, 2011
    Messages:
    3,296
    Resources:
    38
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    3
    JASS:
    4
    Resources:
    38

    • First, I prefer
      apply
      and
      remove
      for the function names
    • Thread crash could occurs on create method if
      growUpDuration
      or
      growDownDuration
      is zero (!0 devided by 0 => thread crash). The solution is to skip some/whole actions: if
      growUpDuration
      is zero, then the unit scaled up/down instantly, if
      growDownDuration
      is zero, then the unit will be reset instantly either, if both of them is zero then don't start the timer at all. I believe you can solve this by yourself actually ;) But the point is 0 means instant
    • Code (vJASS):

                  if eDuration < 0 then
                      set growBack = false
                  else
                      set growBack = true
                  endif

      Usually, 0 is also included as permanent so it should be
      eDuration <= 0
      .
    • Code (vJASS):

                          if not pauseGrow then
                              set tmpScale = unitScale[id] + growRate
                              call SetUnitScale(grower, tmpScale, 1, 1)
                              set unitScale[id] = tmpScale

      I think
      tmpScale
      is unneeded (you are not using it enywhere else)
      Code (vJASS):

                          if not pauseGrow then
                              set unitScale[id] = unitScale[id] + growRate
                              call SetUnitScale(grower, unitScale[id], 1, 1)

    • Code (vJASS):

                  set updatesUp = R2I((scaleGoal - scaleStart) /growRate)
                  set updatesDown = -1   // Just a negative value. Mustn't be 0 or bigger at initialization.

      It's been quite hard to understand what those two variables do. But I have came to a conclusion that one boolean variable, named
      state
      or something, could covers both of their jobs.

      For example, when
      state
      is true that means unit is still growing up and vice versa. To check whether a unit has finished growing:
      Code (vJASS):

      if state then
          if (growRate < 0 and currentScale <= targetScale) or (growRate > 0 andcurrentScale >= targetScale) then
              // This means unit has finished growing

              set currentScale = targetScale // To makes sure the scale doesn't exceed the target
              call SetUnitScale(...)
          endif
      elseif not pause then
          // When unit is being reset-ed
      else
          // Effect duration
      endif

    I will look further into it later, sudden busy :p

    EDIT:
    One more :p

    Code (vJASS):

                if growCount[id] == 1 then
                    set isGrower[id] = false
                    set growCount[id] = 0
                else
                    set growCount[id] = growCount[id] - 1
                endif

    =>
    Code (vJASS):

                if growCount[id] == 1 then
                    set isGrower[id] = false
                endif
                set growCount[id] = growCount[id] - 1
     
  5. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    these are not available, therefore they are not approved, therefore yours cant be really approved, because you cant use unapproved(even better, removed/deleted) depedencies
     
  6. Mythic

    Mythic

    Media Manager

    Joined:
    Apr 24, 2012
    Messages:
    8,170
    Resources:
    130
    Models:
    111
    Icons:
    5
    Maps:
    5
    Spells:
    6
    Reforged HD Icons:
    1
    Tutorials:
    2
    Resources:
    130
    This is extremely useful though. I hope that case is clarified.
     
  7. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    These resources were approved in the past and work reliable afaik. In my eyes it's not a problem using them as dependencies.
     
  8. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    its pain in butt to get them tho, because analrus decided to migrade to github.
     
  9. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    set growBack = (eDuration > 0.)
    :thumbs_up:
     
  10. Kazeon

    Kazeon

    Joined:
    Oct 12, 2011
    Messages:
    3,296
    Resources:
    38
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    3
    JASS:
    4
    Resources:
    38
    Code (vJASS):
        struct DefaultScale
            private static key k
            private static Table table = k
            readonly integer unitType
            readonly real    defaultScale
           
            static method create takes integer uType, real scale returns thistype
                local thistype this = table[uType]
                if this == 0 then
                    set this = allocate()
                    set table[uType] = this
                endif
                set unitType = uType
                set defaultScale = scale
                return this
            endmethod
           
            static method operator [] takes integer i returns thistype
                return table[i]
            endmethod
           
        endstruct
     

    And they are all should be privatized since I feel it's an awful way to get unit's default scale. I mean, we should have a variable
    DefaultScale
    for every unit type we register there? No way

    You should add another function bloat called GetUnitDefaultScale(unit) :v
     
  11. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,532
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Hm, if it helps... my test map contains them. :D

    Thanks. :)

    Nice one.^^

    Indeed, that should be added! :csmile: And thanks for other suggestions, I will have a look at them.

    Thank you! Sorry, what is Tiny's Grow?
     
  12. Kazeon

    Kazeon

    Joined:
    Oct 12, 2011
    Messages:
    3,296
    Resources:
    38
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    3
    JASS:
    4
    Resources:
    38
    Guess you are the only human on earth who don't know dota :v
     
  13. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,532
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Oh, haha. Then, indeed I don't know it... :razz:
     
  14. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
  15. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,532
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    @Dalvengyr
    You were right about the "zero durtion" thread crash, thanks! I do not allow now durations equal or smaller 0 anymore, because it anyway does not make sense.
    People can use the
    SetUnitScale
    native for this.
    I was already messing around a bit with this, but came to conclusion better to use two variables.
    The reason for this was easier handling with accuracy (near targetScale) when unit is affected by multiple scale changes at same time.
    (at least how I tested it... if you are sure there is a better way that won't let accuracy suffer too much under multiple instances I will try harder :D)

    In case I use
    set growCount[id] = 0
    it's faster than
    set growCount[id] = growCount[id] - 1
    and same result in end. :p

    I only quoted things I wanted to answer to. Implemented almost all other suggestions, thanks!. :csmile:

    Updated.

    @edo
    Thanks for the link.^^
     
  16. Maker

    Maker

    Joined:
    Mar 6, 2006
    Messages:
    9,174
    Resources:
    17
    Maps:
    2
    Spells:
    14
    Tutorials:
    1
    Resources:
    17
    In my opinion you should allow 0 duration and make it instant.
     
  17. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,532
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Implemented now.
     
  18. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,846
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    ** Note:
    ** The system does not allow take 0 as argument for durations.

    erm...nope? :D

    also, you have at least 10 candidates for generating trigger evaluations, lets play a Hide and seek shall we? :D

    Edit: actually, only 6.

    + Indention is wrong inside
    function ResetScale takes unit u, real duration returns nothing
     
  19. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,532
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    The note was outdated, thanks. :D

    And also moved the API! Edo-update done.