• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Snippet] Dynamic Timer

The are many timer systems out there and I dont want to compete with them as it spams a lot nowadays and this is heavily
inspired by Nesthauru's CTL except it uses different intervals instead of only 0.03125 or constant, the cons of
this one is that it creates multiple timers like TimedLoop (by vexorian)...

The difference is that you dont need to manually deallocate the instance and this uses only 3 implementations, 1 is optional...


JASS:
/*
===Dynamic Timer v1.2
===Created by Mckill2009

INPIRED BY:
- CTL by Nesthaurus
- Dynamic GUI Indexing by Hanky

API: Module
    implement DTloop
        - Not optional, this needs to be on TOP of implementations
        - You may declare your locals here
        - This is where your condition is generated/running, this by default is TRUE
        - If it encounters FALSE on the first "if", statement, it will automatically deindexed
        - Take note that you dont need to put an "endif" to your condition
                            Sample:
                                implement DTloop
                                    local unit x = GetUnitX(.hero)
                                    local unit y = GetUnitY(.hero)
                                    if UnitAlive(.hero) then
                                        YOUR ACTIONS HERE!
                                implement DTend                              
   
    implement DTnulls
        - Optional, used to null your local variables created via DTloop
    
    implement DTend
        - Not optional, this needs to be BELOW of DTloop or implementations
        - This calls the "create(real variable)", this is where a timer interval is dynamic      

API: Methods
    static method create takes real interval returns thistype
        - By default, timer runs at INTERVAL (0.03125)
        - The "interval" simply delays the 0.03125

CREDITS:
- Deaod for suggesting standard methods
*/

library DynamicTimer

globals
    //These should NOT be modified
    private constant integer MAX_INDEX = 8190
    private constant real INTERVAL = 0.03125
endglobals

module DTloop //not optional
    readonly boolean bol
    readonly real gap
    readonly real in
    static boolean chkindex = true
    static integer index1 = 0 //
    static integer array indexAr
    static timer t
    
    method chk takes nothing returns boolean
endmodule

module DTnulls //optional
            return true
        endif
endmodule

module DTend //not optional
                implement DT_DO_NOT_IMPLEMENT_THIS
                endif
            endif
            set looper = looper + 1
            exitwhen looper > index1
        endloop
    endmethod

    static method create takes real interval returns thistype
        local thistype this
        if (index1 < MAX_INDEX) then
            set this = allocate()
            set .gap = 0
            set .in = interval
            set .bol = true
            if index1==0 then
                set t = CreateTimer()
                call TimerStart(t,INTERVAL,true,function thistype.looper)
            endif
            set index1 = index1 + 1
            set indexAr[index1] = this
        elseif chkindex then
            set chkindex = false
            debug call BJDebugMsg("ERROR: Struct has "+ I2S(MAX_INDEX)+ " index, unable to allocate!: TIMER DESTROYED.")
            call PauseTimer(t)
            call DestroyTimer(t)
        endif       
        return this
    endmethod
endmodule

//NEVER IMPLEMENT THIS
module DT_DO_NOT_IMPLEMENT_THIS //XD
    implement DTnulls
        set .bol = false
        return .bol
    endmethod
    
    static method looper takes nothing returns nothing
        local thistype this
        local integer looper = 1
        loop
            set this = indexAr[looper]
            set .gap = .gap + INTERVAL
            if .gap > .in then
                set .gap = 0
                call chk()
                if not .bol then
                    call .destroy()
                    set indexAr[looper] = indexAr[index1]
                    set index1 = index1 - 1    
                    if index1==0 then
                        call PauseTimer(t)
                        call DestroyTimer(t)
                    endif
endmodule

endlibrary



JASS:
library DT uses DynamicTimer optional Alloc

native UnitAlive takes unit u returns boolean

struct DT //extends array
    //you may also use custom allocators
    //implement Alloc
    unit u
    real dur
    static thistype d
    static real interval = 1.8
    
    method destroy takes nothing returns nothing
        call BJDebugMsg("HERO NAME ====="+GetUnitName(.u))
        set .u = null
        call BJDebugMsg("HERO NAME ====="+GetUnitName(.u))
        call .deallocate()
    endmethod

    implement DTloop
        local texttag tag
        local real du
        if .dur > 0 and UnitAlive(.u) then
            set .dur = .dur - 0.5
            set du = .dur
            set tag = CreateTextTag()
            call SetTextTagPosUnit(tag, .u, 0)
            call SetTextTagText(tag, R2S(du), 0.025)
            call SetTextTagPermanent(tag, false)
            call SetTextTagVelocity(tag, 0.03, 0.03)
            call SetTextTagLifespan(tag, 3)
            call SetTextTagFadepoint(tag, 0.01) 
            //or best to null the local "tag" here XD
            set tag = null
        //no need to put "endif" coz if this will encounter FALSE
        //it will automatically be deindexed
        
    implement DTnulls //optional
        //null locals here
        set tag = null
    
    implement DTend
 
    static method cast takes nothing returns boolean
        local thistype this
        local unit hero = GetTriggerUnit()
        local unit first
        call GroupEnumUnitsInRange(bj_lastCreatedGroup,GetUnitX(hero),GetUnitY(hero),1000.,null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first==null
            if interval > 0.3 then
                set interval = interval - 0.3
            endif
            set this = create(interval)
            set .u = first
            set .dur = GetRandomReal(5.,10.)
            call GroupRemoveUnit(bj_lastCreatedGroup,first)
        endloop
        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function thistype.cast))
    endmethod
endstruct

endlibrary



v1.1
- Changed many API name and structures
- Automatically deallocates instance when done
- Added optional nulling of locals and instance

v1.2
- API name changed so that it can use standard and custom allocators
- DTendnulls removed, codes shortened
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
816
Just use the following code in the future.

Unlike this submission, it works reliably, doesnt fuck with your mind, and actually has meaningful names (which are unlikely to collide within the namespace).

It also is customizable should you need a solution more specific to your problem.

JASS:
library SpellTemplate
    
    private struct Data
        //
        // actual struct members
        //
        
        private integer loopIndex
        
        private static thistype array loopInstances
        private static integer loopInstancesCount=0
        private static timer LOOP_TIMER=CreateTimer()
        private static constant real LOOP_TICK = 1./40
        
        private method destroy takes nothing returns nothing
            //
            // clean your struct here
            //
            set loopInstancesCount=loopInstancesCount-1
            set loopInstances[loopIndex]=loopInstances[loopInstancesCount]
            set loopInstances[loopIndex].loopIndex=loopIndex
            if loopInstancesCount==0 then
                call PauseTimer(LOOP_TIMER)
            endif
            call deallocate()
        endmethod
        
        private static method callback takes nothing returns nothing
        local integer i=loopInstancesCount-1
        local thistype this
            loop
                exitwhen i<0
                set this=loopInstances[i]
                //
                // do your things here, dont forget to call destroy() somewhen
                //
                set i=i-1
            endloop
        endmethod
        
        static method create takes nothing returns thistype
        local thistype this=allocate()
            //
            // initialize the struct here
            //
            set loopInstances[loopInstancesCount]=this
            set loopIndex=loopInstancesCount
            if loopInstancesCount==0 then
                call TimerStart(LOOP_TIMER, LOOP_TICK, true, function thistype.callback)
            endif
            set loopInstancesCount=loopInstancesCount+1
            return this
        endmethod
    endstruct

endlibrary
 
@Deaod
the code you gave doesnt work on extended aray unless you implement custom allocators,
mine does, besides its a pain in the yes to cnp all those codes than just implementing ;)...
JASS:
struct SpellTest extends array
    // declare members
    implement DTloop
        //declare locals 
        if true then 
            //running conditions
            //no need to put endif for the first IF statement     
            //if it encounters false, it will be deindexed  
    implement DTend
        //no need to put anything below this implementation

    static method cast takes nothing returns nothing
        local thistype this = getIndex(0.5)
        //declare instance
    endmethod
endstruct

@Bribe
private gives error, if I put it inside the module...about the ban, oh well, I
would like to improve this and be glad to put in the graveyard :)...

@Nes
Like I said, there's nothing wrong with HT...


====
Updated v1.1
 
Level 14
Joined
Nov 18, 2007
Messages
816
Yes, its really illogical. If you can recognize that, why dont you change your behaviour accordingly?

Also, i have yet to see an allocation/deallocation algorithm better than the one JASSHelper uses. Bonus points if you can actually understand how it works.
 
I use normal allocators in my WarChasers II map because I don't see the point in coding/including a bunch of stuff that already exists in the compiler which also detracts from readability.

The only time I use structs that extend array now is when the allocator is either nonexistent or not so simple, or when in a public resource just for OCD's case (like Mag mentioned).
 
I'm so used to writing it out.
I just can't help it anymore.

Whenever I type out 'struct XXXX extends array', I immediately write out the globals and the create method with the allocation algorithm along with the destroy method containing the deallocation algorithm.

Yes, I know how it works.

If you guys really insist on it, I'll just use thistype.allocate()/this.deallocate() in /public/ resources from now on.
 
We really don't need anymore timer systems :L

Using this in a map is totally fine, but we don't need to have it here.

TimerUtils alone is enough as a universal timer system (While not taking into account speed).

CTL and T32 complete the timer system set by optimizing fast periodic timers.

TimerTools by Nes puts the cherry and chocolate syrup on top by using a nice merging algorithm to speed up everything.
 
Top