• 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.

[vJass] TimedEffects

Level 18
Joined
Jan 21, 2006
Messages
2,552
I was searching this forum for a timed-effects library (because I use it frequently in my spells, others may too) but there didn't appear to be one. I submitted code for a timed-effects library ages ago, but it was lacking in usefulness. So, centuries later, I am re-submitting this library that I've since updated to gain certain benefits, at the loss of inconsequential accuracy.

Requirements
  • JassNewGen Pack (with most recent version of JassHelper)

Pros
  • Only uses a single timer, along with a stack that is updated periodically.
  • Very secure (not that this is ever much of an issue), you will not be able to stunt the functionality of this code without changing the code itself. Good use of scope definitions.
  • Doesn't require anything but the JassNewGenPack to compile.
  • Well it does its job, and there doesn't appear to be anything in the site's data-base.

Cons
  • Because it only uses a single timer, you have to sacrifice the dynamic element of using multiple timers and replace it with a static periodic timer. The difference in accuracy between having an exact timer and using only one is unnoticeable.

JASS:
library TimedEffects

globals
//***************
// Config
//
//  [refreshAccuracy] defines the maximum time (in seconds) in between a timedeffect's "real" expiration and when
//  it is actually destroyed. This is a side-effect of only using one timer to update the stack of effects. It is
//  possible to use a linkedlist and use a dynamic expiration, but that ends up costing too much in efficiency.
    public constant real    refreshAccuracy     = 0.05
//
//
endglobals


//*****************
// Timed Effects
//
struct s_timedeffect
    private effect      p_model     = null
    // the user has no real need to reference this, because if he/she needs to prematurely destroy the
    // effect, he should use the 's_timedeffect.destroy' method.
    
    readonly real       timeTotal    
    readonly real       timeRemaining
    // the user can reference these (for whatever reason) but he cannot change their values. they are internal components
    // but i thought it wasn't any good keeping it from the user for no reason.
    
    private integer     p_index
    
    private static thistype array   stack
    private static integer          stackN          = 0
    
    private static timer            stackRefresh    = CreateTimer()
    // stackRefresh updates on the interval defined by [refreshAccuracy] in the 'config' declarations
    
    method onDestroy takes nothing returns nothing
        set thistype.stackN = thistype.stackN - 1
        set thistype.stack[this.p_index] = thistype.stack[thistype.stackN]
        set thistype.stack[this.p_index].p_index = this.p_index
        
        // if [thistype.stackN] is 0, then there are no more effects to be updated and we can pause the timer.
        if(thistype.stackN==0) then
            call PauseTimer(thistype.stackRefresh)
        endif
        
        // if the user is destroying the effect else-where, the variable will be null
        if(p_model != null) then
            call DestroyEffect(this.p_model)
        endif
    endmethod
    
    
    
    static method onRefresh takes nothing returns nothing
        local integer i
        local thistype fx
        
        set i = thistype.stackN - 1
        loop
            exitwhen(i < 0)
            set fx = thistype.stack[i]
            
            if(fx != 0) then // precaution
                set fx.timeRemaining = fx.timeRemaining - refreshAccuracy
                if(fx.timeRemaining <= 0.00) then
                    // when the countdown has reached 0.00, the timed effect must be destroyed
                    call fx.destroy()
                endif
            endif
            set i = i-1
        endloop
    endmethod
            
    
    
    static method create takes effect e, real duration returns thistype
        local thistype fx = thistype.allocate()
        
        set fx.p_model = e                  
        set fx.timeRemaining = duration     
        set fx.timeTotal = duration
        
        // initially, the timer is not started but we don't want it running unnecessarily. once the first timedeffect
        // has been created, we can start the timer. likewise, if there are no more timedeffects left, we pause the timer (onDestroy)
        if(thistype.stackN==0) then
            call TimerStart(thistype.stackRefresh, refreshAccuracy, true, function thistype.onRefresh)
        endif
        
        // add the timed effect to the stack so that they are updated properly.
        set fx.p_index = thistype.stackN
        set thistype.stack[thistype.stackN] = fx
        set thistype.stackN = thistype.stackN+1
        
        return fx
    endmethod
endstruct
//
//
endlibrary
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Oh. Okay. No biggie. I actually have this library in my Spell/Ability Request thread map-upload, so I thought I'd be doing everyone a favor. Its funny because I was thinking to myself while posting this: "I could really take out the 'effect' part and just add a duration to any 'handle'". The only down-side to TimedHandles is that it uses multiple timers, which pretty much doubles your handle count. It probably evens out in the end.
 
Level 8
Joined
Oct 3, 2008
Messages
367
Processing time is far more important than memory. A single timer is not faster than multiple timers (with low frequencies such as those required by this). Plus there's the multiple timers being more accurate.

And since we have something for timed effects already in the database, I'm pretty much ready to send this to the graveyard.
 
Top