• 🏆 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!

TimedUnitScale v2.0

Info

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

Code
JASS:
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
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
Level 19
Joined
Mar 18, 2012
Messages
1,716
You don't have to list requirements of requirements. It's annoying, if a user wants to change
for example the UnitIndexer system. He will have to check the whole code, if Event or
WorldBounds are used in your system. Takes one seconds for me, but for someone who
doesn't know what Event does or is simply not so experienced, it will be a horrible trip.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you have call TimerStart(thistype.tim, thistype.TIMEOUT, true, function thistype.callback) in TimedUnitScale.ceate, but TimedUnitScale.callback is defined 33 lines below that point, so I wonder how does this even compile :D -> move callback above, could generate some nasty things like trigger evals
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Yes I saw this too the first time I checked your code. However left uncommented, because
such things very often seen in already approved snippets.
I think a few people abuse this vJass feature to provide a better read-ability. ( Creators on top )
It's a good practise to obtain in vJass the same order, as in plain JASS or with functions.
For instance anarchons full screen inventory system generates hundreds of lines of extra code,
just because he didn't care about putting methods in a good order.
 
Top