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

SpellMacro

Allows you to automatically create spell structs.
JASS:
library SpellMacro

    /*
        Allows you to automatically create spell structs
       
        Thanks to Bribe for SpellEffectEvent
        
        Warning:
            All spells must be inside a library and the library must require SpellEffectEvent
            The two methods for cast and periodic effect must be named cast and periodic
            method shall be inside a module
    */
    
    //! textmacro NEW_SPELL takes NAME, MODULE, ABILITY
        private struct $NAME$ extends array
        
            static integer array n
            static integer array p
            static integer ic = 0
            static integer array r
            static integer c = 0
            static timer t = CreateTimer()
            static real TIMEOUT = 0.031250000
            
            static method allocate takes nothing returns $NAME$
                local $NAME$ this = r[0]
                if 0 == this then
                    set ic = ic + 1
                    return ic
                endif
                set r[0] = r[this]
                return this
            endmethod
            
            method deallocate takes nothing returns nothing
                set r[this] = r[0]
                set r[0] = this
            endmethod
            
            method add takes nothing returns nothing
                set n[this] = 0
                set p[this] = 0
                set n[p[0]] = this
                set p[0] = this
            endmethod
            
            method remove takes nothing returns nothing
                set n[p[this]] = n[this]
                set p[n[this]] = p[this]
            endmethod
            
            method destroy takes nothing returns nothing
                call this.remove()
                call this.deallocate()
                set c = c - 1
                if 0 == c then
                    call PauseTimer(t)
                endif
            endmethod
            
            implement $MODULE$
            
            static method update takes nothing returns nothing
                local $NAME$ this = n[0]
                loop
                    exitwhen 0 == this
                    call this.periodic()
                    set this = n[this]
                endloop
            endmethod
            
            static method run takes nothing returns boolean
                local $NAME$ this = $NAME$.allocate()
                call this.add()
                call this.cast()
                set c = c + 1
                if 1 == c then
                    call TimerStart(t, TIMEOUT, true, function $NAME$.update)
                endif
                return false
            endmethod
            
            static method onInit takes nothing returns nothing
                call RegisterSpellEffectEvent($ABILITY$, function $NAME$.run)
            endmethod
            
        endstruct
    //! endtextmacro
endlibrary

Demo:
JASS:
library Test requires SpellEffectEvent SpellMacro
    module Test
        static unit array u
        static real array d
        static integer clap = 'AHtc'
        method periodic takes nothing returns nothing
            if 0 < d[this] then
                set d[this] = d[this] - TIMEOUT
                call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl", GetUnitX(u[this]), GetUnitY(u[this])))
            else
                call this.destroy()
                set u[this] = null
                set d[this] = 0
            endif
        endmethod
    
        method cast takes nothing returns nothing
            set u[this] = GetTriggerUnit()
            set d[this] = 5
        endmethod
    endmodule

    //! runtextmacro NEW_SPELL ("Clap", "Test", "clap")
endlibrary

TestMap:
 

Attachments

  • Test.w3x
    16.2 KB · Views: 52
This is essentially a periodic .03125 timer latched directly on to spell effect event.

JASS:
struct Test extends array
    private unit source
    private real period

    implement CTLExpire
        call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl", GetUnitX(source), GetUnitY(source)))
        set period = period - .03125
        if (period < .03125) then
            set source = null
            call destroy()
        endif
    implement CTLEnd
    
    private static method cast takes nothing returns nothing
        local thistype this = create()
        set source = GetTriggerUnit()
        set period = 5
    endmethod
    
    private static method onInit takes nothing returns nothing
        call RegisterSpellEffectEvent('AHtc', function thistype.cast)
    endmethod
endstruct

vs

JASS:
library Test requires SpellEffectEvent SpellMacro
    module Test
        static unit array u
        static real array d
        static integer clap = 'AHtc'
        method periodic takes nothing returns nothing
            if 0 < d[this] then
                set d[this] = d[this] - TIMEOUT
                call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl", GetUnitX(u[this]), GetUnitY(u[this])))
            else
                call this.destroy()
                set u[this] = null
                set d[this] = 0
            endif
        endmethod
    
        method cast takes nothing returns nothing
            set u[this] = GetTriggerUnit()
            set d[this] = 5
        endmethod
    endmodule

    //! runtextmacro NEW_SPELL ("Clap", "Test", "clap")
endlibrary

CTL Generated Code

JASS:
        static integer rctl32
        static method create takes nothing returns thistype
            return CT(rctl32)
        endmethod
        method destroy takes nothing returns nothing
            call DT(this)
        endmethod
        static method ectl32 takes nothing returns boolean
            local thistype this=rf[rctl32]
            loop
                exitwhen 0==this
                set this=n[this]
            endloop
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            set rctl32 = A(function thistype.ectl32)
        endmethod

vs

JASS:
private struct $NAME$ extends array
            static integer array n
            static integer array p
            static integer ic = 0
            static integer array r
            static integer c = 0
            static timer t = CreateTimer()
            static real TIMEOUT = 0.031250000
            static method allocate takes nothing returns $NAME$
                local $NAME$ this = r[0]
                if 0 == this then
                    set ic = ic + 1
                    return ic
                endif
                set r[0] = r[this]
                return this
            endmethod
            method deallocate takes nothing returns nothing
                set r[this] = r[0]
                set r[0] = this
            endmethod
            method add takes nothing returns nothing
                set n[this] = 0
                set p[this] = 0
                set n[p[0]] = this
                set p[0] = this
            endmethod
            method remove takes nothing returns nothing
                set n[p[this]] = n[this]
                set p[n[this]] = p[this]
            endmethod
            method destroy takes nothing returns nothing
                call this.remove()
                call this.deallocate()
                set c = c - 1
                if 0 == c then
                    call PauseTimer(t)
                endif
            endmethod
            static method update takes nothing returns nothing
                local $NAME$ this = n[0]
                loop
                    exitwhen 0 == this
                    call this.periodic()
                    set this = n[this]
                endloop
            endmethod
            static method run takes nothing returns boolean
                local $NAME$ this = $NAME$.allocate()
                call this.add()
                call this.cast()
                set c = c + 1
                if 1 == c then
                    call TimerStart(t, TIMEOUT, true, function $NAME$.update)
                endif
                return false
            endmethod
            static method onInit takes nothing returns nothing
                call RegisterSpellEffectEvent($ABILITY$, function $NAME$.run)
            endmethod 
        endstruct

DT and CT from CTL (for op comparison)
JASS:
    private function DT takes integer t returns nothing
            set ns[t]=ns[0]
            set ns[0]=t
    endfunction
    private function CT takes integer r returns integer
        local integer i
        local integer f
        if (0==n[0]) then
            set i=ic+1
            set ic=i
        else
            set i=n[0]
            set n[0]=n[i]
        endif
        set th[i]=r
        set ns[i]=-1
        set f=rf[r]
        if (0==f) then
            set n[i]=0
            set p[i]=0
            set rf[r]=i
            set ct[r]=TriggerAddCondition(t,rc[r])
            //set ct[r] = null
            if (0==tc) then
                call TimerStart(m,.031250000,true,function E)
            endif
            set tc=tc+1
        else
            set n[i]=f
            set p[i]=0
            set p[f]=i
            set rf[r]=i
        endif
        return i
    endfunction

DT throws the timer into a stack. The stack is iterated over on the next expiration and the trigger conditions etc are cleaned up. This is to prevent trigger condition errors and linked list errors. This does add a little bit of overhead, but CTL is made for all .03125 timers on a map =), so the overhead is negated when 2+ timers are using CTL.
 
Last edited:
Top