System Key Timers 2

Jesus4Lyf

Good Idea™
Key Timers 2​
Version 1.7.3​

Requirements:
- Jass NewGen

JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ KT ~~ Key Timers 2 ~~ By Jesus4Lyf ~~ Version 1.7.3 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Key Timers?
//         - Key Timers attaches structs to timers, or more to the point timed
//           effects.
//         - You can specify different periods.
//         - Key Timers only uses one timer with one trigger per low period
//           to keep things efficient, especially within the looping.
//         - Key Timers alternatively uses one trigger per instance for all higher
//           periods to allow accurate expirations in a stable and efficient fashion.
//
//    =Pros=
//         - Easy to use.
//         - Fastest attachment loading system (storing in parallel arrays).
//         - Fastest execution system for low periods (all functions on one trigger).
//         - Allows multiple periods to be used.
//         - No H2I. Backwards compatability through patch 1.23 and 1.24.
//
//    =Cons=
//         - The code passed into KT2 must call KT_GetData exactly once.
//         - Periods must be a multiple of 0.00125 seconds. Not 0.007, for example.
//
//    Functions:
//         - KT_Add(userFunc, struct, period)
//         - KT_GetData returns the struct
//
//         - userFunc is to be a user function that takes nothing and returns boolean.
//           It will be executed by the system every period until it returns true.
//
//         - KT_GetData is to be used inside func, it will return the struct passed to
//           to the Add function. It must be called exactly once within the func.
//
//  Details:
//         - KT2 treats low periods and high periods differently, optimizing each
//           with appropriate speed and accuracy, although this effect is invisible
//           to you, the mapper.
//
//         - While func returns false the timer will continue to call it each period.
//           Once func returns true the instance will be detached from system.
//
//  Thanks:
//         - Daxtreme: For encouraging me to return to Key Timers 2, rather than
//           leave it to rot. His interest in the system restored it, and helped
//           it to become what it is now. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" data-shortname=":)" />
//
//         - Captain Griffen: For his work on Rapid Timers, demonstrating that it
//           is possible to attach all functions to one trigger, and that it is
//           indeed faster.
//
//         - Cohadar: Told me to make Key Timers without a textmacro.
//           Thanks to him for helping me with the original Key Timers system too.
//           Also, I'd like to thank him for his work on Timer Ticker (TT) which
//           demonstrated how to use triggers/conditions in this sort of system,
//           which has been used in Key Timers 2.
//
//  How to import:
//         - Create a trigger named KT.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library KT
    ///////////////
    // Constants //
    ////////////////////////////////////////////////////////////////////////////
    // That bit that users may play with if they know what they're doing.
    // Not touching these at all is recommended.
    globals
        // Period Threshold is the point at which Key Timers 2 will switch from
        // using the single timer per period mechanism to using TAZO, which is
        // better for higher periods due to the first tick being accurate.
        private constant real PERIODTHRESHOLD=0.3 // MUST be below 10.24 seconds.
        
        // Tazo's number of precached instances. You can go over this during
        // your map at runtime, but it will probably do some small background
        // processing. Precaching just speeds things up a bit.
        private constant integer TAZO_PRECACHE=64
        
        // Tazo uses the low period part of Key Timers 2 to construct triggers
        // over time when precached ones run out. Here you can set the period used.
        private constant real TAZO_CONSTRUCT_PERIOD=0.03125
    endglobals
    
    //////////////////////////
    // Previous KT2 Globals //
    ////////////////////////////////////////////////////////////////////////////
    // These needed to be moved here for TAZO to hook GetData.
    globals
        private timer array KeyTimer
        private trigger array TimerTrigger
        private integer array KeyTimerListPointer
        private integer array KeyTimerListEndPointer
        private triggercondition array TriggerCond
        private boolexpr array Boolexpr
        private integer array Data
        private integer array Next
        private integer array Prev
        private integer TrigMax=0
        private integer array NextMem
        private integer NextMemMaxPlusOne=1
        private integer array ToAddMem
        private triggercondition array ToRemove
        private boolexpr array ToDestroy
        private boolean array IsAdd
        private integer AddRemoveMax=0
        
        // Locals
        private integer t_id=-1
        private integer t_mem
        private integer t_lastmem
        private integer a_id
        private integer a_mem
        
        // Code Chunks
        private conditionfunc RemoveInstanceCond
    endglobals
    
    //////////////////
    // Previous KT2 //
    ////////////////////////////////////////////////////////////////////////////
    // The KT2 implementation
    private function KeyTimerLoop takes nothing returns nothing
        set t_id=R2I(TimerGetTimeout(GetExpiredTimer())*800)
        set t_mem=KeyTimerListEndPointer[t_id]
        call TriggerEvaluate(TimerTrigger[t_id])
        set t_mem=0
        loop
            exitwhen t_mem==AddRemoveMax
            set t_mem=t_mem+1
            if IsAdd[t_mem] then
                set TriggerCond[ToAddMem[t_mem]]=TriggerAddCondition(TimerTrigger[t_id],Boolexpr[ToAddMem[t_mem]])
            else
                call TriggerRemoveCondition(TimerTrigger[t_id],ToRemove[t_mem])
                call DestroyBoolExpr(ToDestroy[t_mem])
            endif
        endloop
        set AddRemoveMax=0
        set t_id=-1
    endfunction
    
    private function RemoveInstance takes nothing returns boolean
        // Will only fire if code returns true.
        set AddRemoveMax=AddRemoveMax+1
        set IsAdd[AddRemoveMax]=false
        set ToRemove[AddRemoveMax]=TriggerCond[t_lastmem]
        set ToDestroy[AddRemoveMax]=Boolexpr[t_lastmem]
        if Next[t_lastmem]==0 then
            set KeyTimerListEndPointer[t_id]=Prev[t_lastmem]
        endif
        set Prev[Next[t_lastmem]]=Prev[t_lastmem]
        if Prev[t_lastmem]==0 then
            set KeyTimerListPointer[t_id]=Next[t_lastmem]
            if KeyTimerListPointer[t_id]&lt;1 then
                call PauseTimer(KeyTimer[t_id])
            endif
        else
            set Next[Prev[t_lastmem]]=Next[t_lastmem]
        endif
        set NextMem[NextMemMaxPlusOne]=t_lastmem
        set NextMemMaxPlusOne=NextMemMaxPlusOne+1
        return false
    endfunction
    
    private function KTadd takes code func, integer data, real period returns nothing
        set a_id=R2I(period*800)
        
        if KeyTimer[a_id]==null then
            set KeyTimer[a_id]=CreateTimer()
            set TimerTrigger[a_id]=CreateTrigger()
        endif
        
        if NextMemMaxPlusOne==1 then
            set TrigMax=TrigMax+1
            set a_mem=TrigMax
        else
            set NextMemMaxPlusOne=NextMemMaxPlusOne-1
            set a_mem=NextMem[NextMemMaxPlusOne]
        endif
        
        set Boolexpr[a_mem]=And(Condition(func),RemoveInstanceCond)
        if t_id==a_id then
            set AddRemoveMax=AddRemoveMax+1
            set IsAdd[AddRemoveMax]=true
            set ToAddMem[AddRemoveMax]=a_mem
        else
            if KeyTimerListPointer[a_id]&lt;1 then
                call TimerStart(KeyTimer[a_id],a_id/800.0,true,function KeyTimerLoop)
                set KeyTimerListEndPointer[a_id]=a_mem
            endif
            
            set TriggerCond[a_mem]=TriggerAddCondition(TimerTrigger[a_id],Boolexpr[a_mem])
        endif
        set Data[a_mem]=data
        
        set Prev[a_mem]=0
        set Next[a_mem]=KeyTimerListPointer[a_id]
        set Prev[KeyTimerListPointer[a_id]]=a_mem
        set KeyTimerListPointer[a_id]=a_mem
    endfunction
    
    public function GetData takes nothing returns integer // Gets hooked by TAZO.
        set t_lastmem=t_mem
        set t_mem=Prev[t_mem]
        return Data[t_lastmem]
    endfunction
    
    private function KTinit takes nothing returns nothing
        set RemoveInstanceCond=Condition(function RemoveInstance)
    endfunction
    
    //////////
    // TAZO //
    ////////////////////////////////////////////////////////////////////////////
    // KT2 implementation for higher periods (low frequency).
    globals
        private constant integer TAZO_DATAMEM=8190 // Added for KT2 hook. Don't change.
    endglobals
    
    globals
        private conditionfunc TAZO_LoadDataCond
        private conditionfunc TAZO_RemoveInstanceCond
        
        private timer   array TAZO_TrigTimer
        private integer array TAZO_Data
        private boolexpr array TAZO_Boolexpr
        
        private trigger array TAZO_AvailableTrig
        private integer       TAZO_Max=0
        
        private integer       TAZO_ConstructNext=0
        private trigger array TAZO_ConstructTrig
        private integer array TAZO_ConstructCount
    endglobals
    
    globals//locals
        private integer TAZO_ConKey
    endglobals
    private function TAZO_Constructer takes nothing returns boolean
        set TAZO_ConKey=GetData()
        call TriggerExecute(TAZO_ConstructTrig[TAZO_ConKey])
        set TAZO_ConstructCount[TAZO_ConKey]=TAZO_ConstructCount[TAZO_ConKey]-1
        if TAZO_ConstructCount[TAZO_ConKey]==0 then
            set TAZO_Max=TAZO_Max+1
            set TAZO_AvailableTrig[TAZO_Max]=TAZO_ConstructTrig[TAZO_ConKey]
            set TAZO_TrigTimer[TAZO_ConKey]=CreateTimer()
            call TriggerRegisterTimerExpireEvent(TAZO_AvailableTrig[TAZO_Max],TAZO_TrigTimer[TAZO_ConKey])
            return true
        endif
        return false
    endfunction
    
    globals//locals
        private trigger TAZO_DeadTrig
        private integer TAZO_DeadCount
    endglobals
    private function TAZO_Recycle takes nothing returns boolean
        set TAZO_DeadTrig=GetTriggeringTrigger()
        set TAZO_DeadCount=GetTriggerExecCount(TAZO_DeadTrig)
        call TriggerClearConditions(TAZO_DeadTrig)
        call DestroyBoolExpr(TAZO_Boolexpr[TAZO_DeadCount])
        call PauseTimer(TAZO_TrigTimer[TAZO_DeadCount])
        set TAZO_Max=TAZO_Max+1
        set TAZO_AvailableTrig[TAZO_Max]=TAZO_DeadTrig
        return false
    endfunction
    
    private function TAZO_LoadData takes nothing returns boolean
        // KT2 Data Hook
        set t_mem=TAZO_DATAMEM
        set Data[TAZO_DATAMEM]=TAZO_Data[GetTriggerExecCount(GetTriggeringTrigger())]
        // End KT2 Data Hook
        return false
    endfunction
    
    private function InitTrigExecCount takes trigger t, integer d returns nothing
        if d&gt;128 then
            call InitTrigExecCount.execute(t,d-128)
            set d=128
        endif
        loop
            exitwhen d==0
            set d=d-1
            call TriggerExecute(t)
        endloop
    endfunction
    
    globals//locals
        private integer TAZO_AddKey
        private trigger TAZO_AddTrigger
    endglobals
    public function TAZOadd takes code func, integer data, real period returns nothing
        if TAZO_Max==0 then
            // Failsafe.
            set TAZO_ConstructNext=TAZO_ConstructNext+1
            set TAZO_AddTrigger=CreateTrigger()
            set TAZO_AddKey=TAZO_ConstructNext
            call InitTrigExecCount.execute(TAZO_AddTrigger,TAZO_AddKey)
            set TAZO_TrigTimer[TAZO_AddKey]=CreateTimer()
            call TriggerRegisterTimerExpireEvent(TAZO_AddTrigger,TAZO_TrigTimer[TAZO_AddKey])
        else
            set TAZO_AddTrigger=TAZO_AvailableTrig[TAZO_Max]
            set TAZO_AddKey=GetTriggerExecCount(TAZO_AddTrigger)
            set TAZO_Max=TAZO_Max-1
        endif
        set TAZO_Data[TAZO_AddKey]=data
        set TAZO_Boolexpr[TAZO_AddKey]=And(Condition(func),TAZO_RemoveInstanceCond)
        call TriggerAddCondition(TAZO_AddTrigger,TAZO_LoadDataCond)
        call TriggerAddCondition(TAZO_AddTrigger,TAZO_Boolexpr[TAZO_AddKey])
        call TimerStart(TAZO_TrigTimer[TAZO_AddKey],period,true,null)
        if TAZO_Max&lt;10 then
            set TAZO_ConstructNext=TAZO_ConstructNext+1
            set TAZO_ConstructTrig[TAZO_ConstructNext]=CreateTrigger()
            set TAZO_ConstructCount[TAZO_ConstructNext]=TAZO_ConstructNext
            call KTadd(function TAZO_Constructer,TAZO_ConstructNext,TAZO_CONSTRUCT_PERIOD)
        endif
    endfunction
    
    private function TAZOinit takes nothing returns nothing
        set TAZO_LoadDataCond=Condition(function TAZO_LoadData)
        set TAZO_RemoveInstanceCond=Condition(function TAZO_Recycle)
        // Allow for GetData
        set Next[TAZO_DATAMEM]=TAZO_DATAMEM
        set Prev[TAZO_DATAMEM]=TAZO_DATAMEM
        // End allow for GetData
        loop
            exitwhen TAZO_Max==TAZO_PRECACHE
            set TAZO_ConstructNext=TAZO_ConstructNext+1 // The index.
            set TAZO_Max=TAZO_Max+1 // Will be the same in the initialiser as ConstructNext.
            set TAZO_AvailableTrig[TAZO_Max]=CreateTrigger()
            call InitTrigExecCount.execute(TAZO_AvailableTrig[TAZO_Max],TAZO_ConstructNext)
            set TAZO_TrigTimer[TAZO_ConstructNext]=CreateTimer()
            call TriggerRegisterTimerExpireEvent(TAZO_AvailableTrig[TAZO_Max],TAZO_TrigTimer[TAZO_ConstructNext])
        endloop
    endfunction
    
    ///////////////
    // Interface //
    ////////////////////////////////////////////////////////////////////////////
    // Stitches it all together neatly.
    public function Add takes code func, integer data, real period returns nothing
        if period&lt;PERIODTHRESHOLD then
            call KTadd(func,data,period)
        else
            call TAZOadd(func,data,period)
        endif
    endfunction
    
    private module InitModule
        private static method onInit takes nothing returns nothing
            call KTinit()
            call TAZOinit()
        endmethod
    endmodule
    
    private struct InitStruct extends array
        implement InitModule
    endstruct
endlibrary

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//    End of Key Timers 2
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


EXAMPLE SPELLS:
These two spells are the ones I use to test new versions of KT2. They demonstrate the full power of the system in curious ways... ^_^
I figure it's about time I link to them here. Mmm... You will be impressed, I should think, if you view them. :)
Sandman.
Sharl.

About a week before I released this system, Cohadar was telling me to make Key Timers not use a textmacro interface. Hence, KT2.

This turns out to be similar to Timer Ticker in use (although the code is much different and more efficient, oh and I blatantly ripped off his awesome documentation which he doesn't mind), but you can use multiple periods.

This is, at the time of release, the most efficient timer system in existance.

Updates:
Key Timers 2:
- Version 1.7.3: Moved initialiser to a module to fix init related bugs added by JassHelper's later versions.
- Version 1.7.2: Changed the high period mechanism to not need to reconstruct triggers and removed the MAX_INSTANCES for it.
- Version 1.7.1: Commented the constants.
- Version 1.7.0: Replaced KT2's high period mechanism to give it a more consistent efficiency (ironically, the new mechanism requires KT2). Removed the high period 0.01 second inaccuracy.
- Version 1.6.1: Removed the warning for invalid periods. Turns out WC3 can't distinguish as precisely as necessary. This really doesn't matter anyway, as they can't be out by more than 0.00125 of a second.
- Version 1.6.0: Removed the maximum period limit and removed the first procurement inaccuracy issue. Also added warning for invalid periods, although these cannot detect mismatches below 0.001 of a second.
- Version 1.5: Made it stable and reliable, removed the boolexpr memory leaks. Now 22% faster than before. ONE INCOMPATABILITY - if you called KT_GetData more than once, or not at all in your code, it will need to be modified to call KT_GetData exactly once.
- Version 1.4: Switched to putting all attached code on one trigger, using Or boolexprs and modifying GetData. Unofficial release.
- Version 1.3: Optimized and inlined. Is within margin on error in speed to verson 1.1. Estimated 0.2% efficiency gain. :)
- Version 1.2: Wasn't released, pending optimization. Rewritten to use a different data storage system (linked lists), to remove the limit of 400 instances per period and 20 different periods max. The "Add" function should also be faster (not bench tested).
- Version 1.1: Updated to use globals instead of locals for efficiency. Changed attaching method to KT_GetData for efficiency. The old GetTriggerExecCount(GetTriggeringTrigger()) method must be replaced with KT_GetData() now to work. Various other optimizations such as using globals to store certain array values instead of constantly referencing arrays. Much bench testing went into determining these things.
- Version 1.0: Release. Does the same thing as Key Timers 1 but with a nicer interface.
Key Timers 1:
- Version 1.4: For some reason NewGen's Parser wouldn't always catch period variable as private. Unprivatised it.
- Version 1.3: Various adjustments and small optimizations.
- Version 1.2: Removed loop from adding function. Removed if Key==0 from loop.
- Version 1.1: Added constant global for storing the period for user use and spaced out the code nicely.
--- All version 1.x above this line can simply be coppied over eachother for updating. ---
- Version 1.0: Release.

Example Maps Below:
KeyTimers.w3x: A short example of the original Key Timers 1 system (not recommended for download).
KeyTimersFireSpell.w3x: An example of a simple using Key Timers 1 (also not recommended for download unless you want to port something from Key Timers 1 to Key Timers 2, because of the next one).
KeyTimers2.w3x: The one you want to download. Demonstrates Key Timers 2 at work, and is a port of the KeyTimersFireSpell map, so if you need to port something from Key Timers 1, this should help. :) This contains an Version 1.0 of Key Timers 2, not the most recent.
 

Attachments

Jesus4Lyf

Good Idea™
Documentation

I've gone through the trouble of downloading a selection of timer systems, analysed how they work, bench tested them, explained how they work, and written a report on them. Here it is:

Timer Systems in Warcraft III
Written by Jesus4Lyf.
(See attachment.)

I strongly urge all mappers to read this document if you ever use timed effects, because it is a detailed, researched, 13 page discussion about what different timer systems do, which are fastest, and why.

If you simply want to know the conclusions, and you're TERRIBLY impatient and could never read a 13 page document which I've meticulously assembled for you, then you may skip to the last page and see the conclusions. Just don't assume to know everything. :D

Happy mapping! :D

PS. DO NOT reduce this thread into any kind of flamewar over benchtests. Thanks. No one else put the effort in to do benchtests.

Update: V1.02. Fixed a few minor errors, mostly to do with history, thanks to Vexorian. 11 views at time of update.
Update: V1.01. Added bench tests for TimerUtils Red, TimerUtils Blue, HSAS and Raw H2I (aka. "I swear it's the fastest!"). 15 views at time of update.
 

Attachments

Cohadar

master of fugue
JASS:
//==============================================================================
//  Collections -- STRUCT STORAGE SYSTEM BY COHADAR -- v2.1c
//==============================================================================
//
//  PURPOUSE:
//      * Storing structs for spells that require variable number of data
//      * Great for algorithms that need random access.
//      * Able to create 100% MUI spells.
//
//  REFERENCE:
//      * This system was inspired by java Collection interface
//        <a href="http://java.sun.com/docs/books/tutorial/collections/interfaces/collection.html" target="_blank" class="link link--external" rel="nofollow noopener">http://java.sun.com/docs/books/tutorial/collections/interfaces/collection.html</a>
//        <a href="http://java.sun.com/javase/6/docs/api/java/util/Iterator.html" target="_blank" class="link link--external" rel="nofollow noopener">http://java.sun.com/javase/6/docs/api/java/util/Iterator.html</a>
//
//  DETAILS:
//      * Collections are now implemented as static-only classes
//        this removes limitations in number of elements from version 1.3
//
//      * Main purpouse of collections is fast storing and removing of structs,
//        Structs in Collection are NOT ordered in any way.
//        Structs can change place inside collection at any time.
//        Iterators are the only safe way to access collection elements.
//
//      * Do NOT create or destoy Collection or Iterator classes,
//        there is no point to that because all operations are static
//
//      * Each Collection has exactly one iterator (for performanse reasons)
//
//      * Collections are implemented with global array that is created
//        by calling a collection macro inside a trigger scope.
//        you can have 8191 structs stored in one collection.
//
//      * Since collections are just a wrapper around of global array $X$_buff
//        some people will feel the need to inline collection calls.
//        Please do NOT do this unless it is really necessary for performanse.
//        This will almost never be the case, collections are extremely fast.
//
//
//  HOW TO USE:
//      * For a trigger to use collections it MUST be inside a scope.
//        At the beginning of scope insert this macro call
//        //! runtextmacro COLLECTION(&quot;A&quot;)
//
//      * This will create global array and other fields and methods 
//        needed for collection manipulation
//
//      * To prevent lag when collections are first used put this into spells InitTrigger
//        call CollectionA.init()   // This is optional
//
//---------------------------------------------------------------------------
//  BASIC LOOP:  (data is your custom struct)
//---------------------------------------------------------------------------
//
//        call IteratorA.reset()   // &lt;-- positions iterator at the beginning of collection
//        loop
//            exitwhen IteratorA.noNext()
//            set data = IteratorA.next()
//    
//            if SomeExitCondition(data)
//			     call data.destroy()	
//               call IteratorA.remove()
//		      else
//               -- Process data --
//            endif		
//        endloop
//
//---------------------------------------------------------------------------
//  LIST OF FUNCTIONS:  (all methods are static)
//---------------------------------------------------------------------------
//      struct CollectionX
//          .init()
//          .isEmpty() -&gt; boolean
//          .size() -&gt; integer
//          .contains(integer) -&gt; boolean
//          .add(integer) -&gt; boolean
//          .remove(integer) -&gt; boolean
//          .clear()
//      endstruct
//
//      struct IteratorX
//          .reset()
//          .noNext() -&gt; boolean
//          .next() -&gt; integer
//          .remove()
//      endstruct
//---------------------------------------------------------------------------
//
//  HOW TO IMPORT:
//       * Just create a trigger named Collections
//       * convert it to text and replace the whole trigger text with this one
//
//==============================================================================


globals
	integer MAX_COLLECTION_SIZE = 8191
endglobals

//! textmacro COLLECTION takes X, MODIFIER

// Do NOT use this fields directly
globals
	// Collection fields
	private integer array $X$_buff
	private integer $X$_size = 0
	
	// Iterator fields
	private integer  $X$_index = -1   			
	private boolean  $X$_removeOK = false
endglobals

//==============================================================================
$MODIFIER$ struct Collection$X$
	
	// prevents the lag by forcing wc3 to allocate whole array at once
	static method init takes integer size returns nothing
	    set $X$_buff[size-1] = 0
	endmethod
	
	// checks if collection is empty
	static method isEmpty takes nothing returns boolean
	    return $X$_size &lt;= 0
	endmethod
	
	// returns the current size of collection
	static method size takes nothing returns integer
	    return $X$_size
	endmethod
	
	// checks is element e is inside collection
	static method contains takes integer e returns boolean
	    local integer i = 0
		loop
		    exitwhen i &gt;= $X$_size
			if e == $X$_buff<i> then
			    return true
			endif
			set i = i + 1
		endloop
		return false
	endmethod
	
	// adds element e into collection
	static method add takes integer e returns boolean
	    if $X$_size &lt; MAX_COLLECTION_SIZE then
		    set $X$_buff[$X$_size] = e
		    set $X$_size = $X$_size + 1
			return true
		else
		    call BJDebugMsg(&quot;|C00FF0000ERROR: Collection$X$ overflow in scope &quot; + SCOPE_PREFIX)
		    return false
		endif
	endmethod
	
	// removes first element that matches e from collection
	static method remove takes integer e returns boolean
	    local integer i = 0
		loop
		    exitwhen i &gt;= $X$_size
			if e == $X$_buff<i> then
			    set $X$_size = $X$_size - 1
			    set $X$_buff<i> = $X$_buff[$X$_size]
			    return true
			endif
			set i = i + 1
		endloop
		return false	    
	endmethod
	
	// removes ALL elements from collection
	static method clear takes nothing returns nothing
	    set $X$_size = 0
	endmethod
	
endstruct

//==============================================================================
$MODIFIER$ struct Iterator$X$
	// Use before any looping to reset iterator to the beginning of collection
	static method reset takes nothing returns nothing
		set $X$_index = -1
		set $X$_removeOK = false
	endmethod
	
	// returns true if at the end of collection
	static method noNext takes nothing returns boolean
	    return $X$_index &gt;= ($X$_size-1)
	endmethod
	
	// Not safe operation, use only if noNext returns false
	static method next takes nothing returns integer
	    set $X$_index = $X$_index + 1
		set $X$_removeOK = true
	    return $X$_buff[$X$_index]
	endmethod
	
	//This method can be called only once per call to next.
	static method remove takes nothing returns nothing
	    if $X$_removeOK then
			set $X$_size = $X$_size - 1
			set $X$_buff[$X$_index] = $X$_buff[$X$_size]    
			set $X$_index = $X$_index - 1
			set $X$_removeOK = false
		else
		    call BJDebugMsg(&quot;|c00FF0000ERROR: Iterator$X$ attempted invalid remove in scope&quot; + SCOPE_PREFIX)
		endif
	endmethod
	
endstruct

//! endtextmacro

//forced by WE
function InitTrig_Collections takes nothing returns nothing
endfunction
</i></i></i>


I know your fist comment would be that collections do not do the same job as key timers.
It is indeed true, they do a more general job.

I am using this as a core for movement in physics engine in my map btw.
But like I said textmacros are something that people do not like to use.
I know only one person besides me that uses this.
 

Jesus4Lyf

Good Idea™
Interesting. But it is a lot less self contained and less efficient, isn't it? You said you "made a much better version of this system" but yours is less efficient and more timeconsuming to implement. I actually use key timers whenever I make physics engines. For a certain spell animation that makes 55 fragments scatter and bounce using physics, I coded the whole engine in 15 lines. Not a shred of lag, of course.

I'll say it bluntly. Key Timers is designed to save time. To save thought. To be extraordinarily efficient. To get the job done. It shaves the hours off painful, repetative code, because every time I want to make a timed effect, I don't even hesitate, because it takes less than 10 minutes. It takes me longer to trigger the untimed code that triggers the timed effect. To me, this system is a life saver. That's why I uploaded it - because to me its a massive, timesaving innovation. :D

But in the end I guess you're right, people don't like text macros. I consider this worth learning though for those who want to make spells quickly and efficiently.

PS. Wc3c down again = -_-
 

Cohadar

master of fugue
This systems simply have different trade-offs.
It is a specialization vs multipurpose issue.

There are stuff you can do with Collections that are simply impossible with your sys. (that spell in collections demo map being one example)

Key Timers can be used only for timed effects, end of story.
All timed effects involve 0.04 timer (some people like 0.035)
So you don't really need a textmacro for that do you?

Btw your system is not faster than collections
Your Add$SUBJECT$ function has complexity O(n)

When you make it O(1) we can talk further. (it is possible)
 

Jesus4Lyf

Good Idea™
I was gonna reply saying I don't know what you're talking about, but I think I do. You're saying remove the loop, and shuffle the struct instances up when one is removed, so the "next entry" is fixed, and the loop is removed. Oh... and I just realised you can just take the last struct and move it backwards to fill the slot. Thanks for pointing this out. *Goes to update it*

Oh and I agree. It's specialised for timed effects. It's meant to make them super fast to make with quality, so that mappers don't have to think twice about if its worth making them, because they won't lag and they'll take 5 minutes to make instead of 20.

PS. I have used this for every 1 second timed effects. I'm using my execcount storage system for when a unit enters range, and key timers for when that unit leaves range. All generic and loaded on map initialization! :D

PPS. Yay! Optimization improvement! Makes this all worth it. :D
 

Cohadar

master of fugue
This is still a textmacro...


Btw that local boolean p=true is useless because you can simply do this:
JASS:

	if $SUBJECT$TimerMax == 0 then
		call PauseTimer($SUBJECT$Timer)
		set $SUBJECT$TimerStart=true
		set $SUBJECT$TimerMax=0
	endif
 

Cohadar

master of fugue
Ye I bet you would :p

For now there is only one way you will ever see TT (it is the name of sys)
and that is by contributing enough to pyramidal defence to be given a level 2 developer map.

Level 1 map contains only publicly released systems.
ABC, PUI, Collections and similar...

Life is a biach...
 

Jesus4Lyf

Good Idea™
*Shrugs* You said Collections was faster and better too.

The way I'd go about it is creating an extention object (which I know very little about, I'd figure it out) and then have a AddInstanceToTimer(timer, instance) function. Since all structs are integers anyway, everything can be stored in one array. Problem is you can't have over (8000 - buffers) structs attached to timers in -total- and picking that buffer would have to vary from map to map. Even so. I don't see how you can get faster - mine doesn't even have a remove instance function, it just returns a boolean. I assume your way of going about it is MUCH different to mine anyway. :)

Hence, I would be most interested to see how you do it, even though it will probably be useless to me by then.

(Anymore suggestions for improving Key Timers whilst still keeping it as a text macro?)
 

Cohadar

master of fugue
(Anymore suggestions for improving Key Timers whilst still keeping it as a text macro?)
Not really big deals:
What is $SUBJECT$TimerStart variable used for?

General programming advice:
JASS:
        // backward counting simplifies code
        local integer i=$SUBJECT$TimerMax
	loop
		exitwhen i&lt;=0
		if Do$SUBJECT$($SUBJECT$TimerKey<i>) then
   		    set $SUBJECT$TimerKey<i>=$SUBJECT$TimerKey[$SUBJECT$TimerMax]
		    set $SUBJECT$TimerMax=$SUBJECT$TimerMax-1
		endif
		set i=i-1
	endloop</i></i>


and a small stupidity:
JASS:
	if $SUBJECT$TimerMax==0 then  // &lt;--------&lt;&lt;
		call PauseTimer($SUBJECT$Timer)
		set $SUBJECT$TimerStart=true
		set $SUBJECT$TimerMax=0 // &lt;--------&lt;&lt; rofl
	endif
 

Jesus4Lyf

Good Idea™
I actually got a laugh out of that...

Well I might update it some time. :)

Touché, Cohadar, touché.

Thanks for being the only person who actually responded to this at all... o_O

PS. $SUBJECT$TimerStart is used to register whether or not the timer is running, and needs to be started.

PPS. I just removed it because i figured that I can use $SUBJECT$TimerMax==1 instead (since it has 1 added to it just before checking). Also I understand why count down instead of up, but I can't be bothered right now.
 

emjlr3

Change can be a good thing
Staff member
he is most likely the only person who cares
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    I really do not want to move to California otherwise I might consider it.
  • The Helper The Helper:
    yeah California is not anywhere you really want to live
  • The Helper The Helper:
    That is why I did not take the job Blizzard offered me back in the day, there is no way I could have moved my family there on what they were offering, not even close and that was like 20 years ago
  • The Helper The Helper:
    yeah they wanted me on the tech support team when they did not get me they got one of the next MVPs in Dinobot
  • The Helper The Helper:
    Dinobot was one of the youngest of the MVPs tkron probably could have worked for Blizzard but he had a good job in Chicago doing business programming already
  • The Helper The Helper:
    Dinobot probably still works for Blizzard would love to reconnect with that guy
  • The Helper The Helper:
    I wonder what ever happened to Wargasm?
  • The Helper The Helper:
    This new version of Xenforo really is awesome
  • Ghan Ghan:
    Wargasm is still around. He works for the domain registrar where thehelper.net is kept.
  • Varine Varine:
    Is sqrage still around?
  • The Helper The Helper:
    I have not seen him on lately the forum says he was last on 2 years ago
  • The Helper The Helper:
    How are you doing Varine have not seen you around in a minute
  • thewrongvine thewrongvine:
    lol I live in CA
  • thewrongvine thewrongvine:
    I've got some friends in animation department who have been applying for Blizzard, doing interviews and such. They said the workplace seems nice, though now it'd be all remote work I suppose
  • The Helper The Helper:
    good for them I live close to Austin and am trying to get my kids interested in getting into Games but not having much luck. I would never let them move to Cali.
  • The Helper The Helper:
    unless it was huge money
  • The Helper The Helper:
    and even then with the taxes I really could never let them move there
  • thewrongvine thewrongvine:
    Haha fair enough, I have no interest in CA at all as a living place either. Really only here just for the work in LA, but hopefully once I get settled enough in my work, I can move out
    +1
  • Ghan Ghan:
    +1
  • jonas jonas:
    " a traditionally-sized California family of one person ":cool:
    +1

    Staff online

    • Ghan
      Administrator - Servers are fun

    Members online

    Affiliates

    Hive Workshop
    Top