System AuraStruct

Nestharus

o-o
Reaction score
84
This is not compatible with Warcraft 3 Standard Aura Abilities. If you use any aura abilities at all, this will break.
You may only use auras that have None as the valid unit targets (no valid targets), which is what I use in the Demo Map.

Warcraft 3 auras break UnitInRangeEvent

Units that aren't indexed and units with locust will not fire.

JASS:

library AuraStruct /* v2.1.3.0
*************************************************************************************
*
*   An efficient and easy to use module for fully custom aura design.
*
*************************************************************************************
*
*   */uses/*
*
*       */ UnitEvent /*                 hiveworkshop.com/forums/jass-functions-413/extension-unit-event-172365/
*       */ UnitInRangeEvent /*          hiveworkshop.com/forums/submissions-414/unitinrangeevent-205036/
*       */ Tt /*                        hiveworkshop.com/forums/jass-functions-413/system-timer-tools-201165/
*       */ RegisterPlayerUnitEvent /*   hiveworkshop.com/forums/jass-functions-413/snippet-registerplayerunitevent-203338/
*
************************************************************************************
*
*   static method getActiveAuraCount takes UnitIndex unitId returns integer
*   static method getAuraCount takes UnitIndex unitId returns integer
*   static method getUnitsUnderAura takes UnitIndex sourceId returns integer
*   static method getUnitsUnderActiveAura takes UnitIndex sourceId returns integer
*
*   static method createAura takes UnitIndex sourceId returns nothing
*       -   Call this when the unit initially gets the aura. This can also be
*       -   called whenever the aura's level changes for an instant update.
*
*   Interface
*
*       private static constant boolean ANIMATED_BESTOW_AURA
*           (required) -   Determine whether animated units bestow auras or not
*
*       private static constant real TIMEOUT
*           (required) -   How often the aura effect runs
*
*       private static constant boolean STACKS
*           (required) -   Does the aura stack?
*
*       private static method getLevel takes UnitIndex sourceId returns integer
*           (required) -    Returns the level of the aura on the unit
*
*       private static method onLevel takes UnitIndex source, integer level returns nothing
*           (optional) -    Runs when aura levels up
*
*       private static method getRange takes UnitIndex source, integer level returns real
*           (required) -    Should return the range of the aura
*
*       private method onEndEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
*           (optional) -    Runs when the aura effect ends (aura no longer on unit)
*
*       private method onEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
*           (optional) -    Runs when aura effect starts (aura just went on to unit)
*
*       private method onPeriodicEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
*           (optional) -    Runs every period of the aura. First run is right after onEffect
*
*       private static method absFilter takes UnitIndex source, UnitIndex entering returns boolean
*           (optional) -    Runs when the unit initially enters in the range of the aura. If this returns false, the
*                      -    unit is ignored as if it doesn't exist and it will never be able to get the aura
*
*       private method filter takes UnitIndex source, UnitIndex affected, integer level returns boolean
*           (optional) -    Runs whenever the aura cycles (every TIMEOUT seconds). This helps determine if the
*                      -    aura is active or not for the unit.
*
*       (it is either both of these at once or neither of them)
*       private static method removeBuff takes unit whichUnit, integer level returns nothing
*           (optional) -    Runs when the buff icon should be removed from the unit
*
*       private static method addBuff takes unit whichUnit, integer level returns nothing
*           (optional) -    Runs when the buff icon should be added to the unit
*
************************************************************************************/
    globals
        private integer array affectedLevel
        private boolean array active
        private UnitIndex array source
        private UnitIndex array affected
    endglobals
    
    private function InitModule takes code index, code deindex, code death, code revive, boolean animatedBestow returns nothing
        call RegisterUnitIndexEvent(Condition(index), UnitIndexer.INDEX)
        call RegisterUnitIndexEvent(Condition(deindex), UnitIndexer.DEINDEX)
        
        call UnitEvent.START_REINCARNATE.register(Condition(death))
        call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, Condition(death))
        
        call UnitEvent.REINCARNATE.register(Condition(revive))
        call UnitEvent.RESURRECT.register(Condition(revive))
        
        if (animatedBestow) then
            call UnitEvent.ANIMATE.register(Condition(revive))
        endif
    endfunction
    
    module AuraStruct
        private static integer array buffCount
        private static integer array auraCount
        private static integer array auraInstance
        private static integer array activeAura
        private static integer array onEnterT
        private static integer array onEnterT2
        private static integer array auraLevel
        private static integer array level
        private static thistype array next
        private static thistype array prev
        private static Table array affecting
        private static real array range
        private static integer array sourceAffected
        
        static method getActiveAuraCount takes UnitIndex affectedId returns integer
            return buffCount[affectedId]
        endmethod
        
        static method getAuraCount takes UnitIndex affectedId returns integer
            return auraCount[affectedId]
        endmethod
        
        static method getUnitsUnderAura takes UnitIndex sourceId returns integer
            return auraInstance[sourceId]
        endmethod
        
        static method getUnitsUnderActiveAura takes UnitIndex sourceId returns integer
            return activeAura[sourceId]
        endmethod
        
        private method isActive takes integer af, integer so returns boolean
            static if not thistype.STACKS then
                static if thistype.filter.exists then
                    return filter(so, af, level[so]) and (level[so] > auraLevel[af] or sourceAffected[af] == this)
                else
                    return level[so] > auraLevel[af] or sourceAffected[af] == this
                endif
            else
                static if thistype.filter.exists then
                    return filter(so, af, level[so])
                else
                    return true
                endif
            endif
        endmethod
        
        private static method removeCheck takes UnitIndex sourceId returns nothing
            set next[prev[sourceId]]=next[sourceId]
            set prev[next[sourceId]]=prev[sourceId]
            
            call sourceId.unlock()
        endmethod
        
        private static integer array ho
        private static boolean array wa
        private static integer array ht
        private static integer array htc
        
        implement CTM
            local boolean wasActive
            local UnitIndex so
            local UnitIndex af
            local integer lev
            local integer array l
            local integer hc = 0
        implement CTMExpire
            set so = source[this]
            set af = affected[this]
            set lev = level[so]
            
            if (/*
                    */0 == lev or /*
                    */not IsUnitInRange(GetUnitById(so), GetUnitById(af), range[so]) /*
            */) then
                set auraCount[af] = auraCount[af] - 1
                set auraInstance[so] = auraInstance[so] - 1
                
                if (active[this]) then
                    set buffCount[af] = buffCount[af] - 1
                    set activeAura[so] = activeAura[so] - 1
                    
                    static if thistype.addBuff.exists then
                        if (0 == buffCount[af]) then
                            call removeBuff(GetUnitById(af), auraLevel[af])
                        endif
                    endif
                    
                    if (sourceAffected[af] == this) then
                        set auraLevel[af] = 0
                        set sourceAffected[af] = 0
                    endif
                    
                    static if thistype.onEndEffect.exists then
                        call onEndEffect(so, af, affectedLevel[this])
                    endif
                endif
                
                call destroy()
                
                if (0 != affecting[so]) then
                    call affecting[so].remove(af)
                endif
                
                call so.unlock()
                call af.unlock()
            elseif (affectedLevel[this] == level[so]) then
                set wasActive = active[this]
                set active[this] = isActive(af, so)
                
                if (active[this]) then
                    if (lev > auraLevel[af]) then
                        if (lev > l[af]) then
                            set l[af] = lev
                            if (0 == ht[af]) then
                                set ht[af] = hc
                                set htc[hc] = af
                                set ho[hc] = this
                                set wa[hc] = wasActive
                                set hc = hc + 1
                            else
                                set hc = ht[af]
                                set ho[hc] = this
                                set wa[hc] = wasActive
                            endif
                        endif
                        
                        static if thistype.STACKS then
                            static if thistype.onPeriodicEffect.exists then
                                call onPeriodicEffect(so, af, lev)
                            endif
                        endif
                    else
                        static if thistype.onPeriodicEffect.exists then
                            call onPeriodicEffect(so, af, lev)
                        endif
                    endif
                elseif (wasActive) then
                    set buffCount[af] = buffCount[af] - 1
                    set activeAura[so] = activeAura[so] - 1
                    
                    static if thistype.addBuff.exists then
                        if (0 == buffCount[af]) then
                            call removeBuff(GetUnitById(af), auraLevel[af])
                        endif
                    endif
                    
                    if (sourceAffected[af] == this) then
                        set auraLevel[af] = 0
                        set sourceAffected[af] = 0
                    endif
                    
                    static if thistype.onEndEffect.exists then
                        call onEndEffect(so, af, affectedLevel[this])
                    endif
                endif
            endif
        implement CTMNull
            loop
                exitwhen 0 == hc
                set hc = hc - 1
                set ht[htc[hc]] = 0
                set this = ho[hc]
                
                set so = source[this]
                set af = affected[this]
                set lev = level[so]
                
                static if not thistype.STACKS then
                    set wasActive = wa[hc]
                    
                    if (not wasActive) then
                        set buffCount[af] = buffCount[af] + 1
                        set activeAura[so] = activeAura[so] + 1
                    endif
                endif
                
                static if thistype.addBuff.exists then
                    if (0 < buffCount[af]) then
                        call removeBuff(GetUnitById(af), auraLevel[af])
                    endif
                endif
                
                static if not thistype.STACKS then
                    static if thistype.onEndEffect.exists then
                        if (wasActive) then
                            call onEndEffect(so, af, affectedLevel[this])
                        else
                            if (0 != sourceAffected[af]) then
                                set active[sourceAffected[af]] = false
                                call thistype(sourceAffected[af]).onEndEffect(source[sourceAffected[af]], af, auraLevel[af])
                            endif
                        endif
                    endif
                    
                    set sourceAffected[af] = this
                    set auraLevel[af] = lev
                endif
                
                static if thistype.addBuff.exists then
                    call addBuff(GetUnitById(af), lev)
                endif
                
                static if not thistype.STACKS then
                    static if thistype.onEffect.exists then
                        call onEffect(so, af, lev)
                    endif
                endif
            endloop
        implement CTMEnd
        
        private static method doEnter takes UnitIndex sourceId, UnitIndex entering returns nothing
            local thistype time = affecting[sourceId][entering]
            
            local boolean canEnter
            local boolean wasActive
            
            local integer buffs
            
            local integer lev
            
            static if thistype.absFilter.exists then
                set canEnter = absFilter(sourceId, entering)
            else
                set canEnter = true
            endif
            
            if (0 != entering and GetUnitUserData(GetUnitById(entering)) == entering and canEnter and (0 == time or affectedLevel[time] != level[sourceId])) then
                if (0 == time) then
                    call entering.lock()
                    call sourceId.lock()
                    
                    set time = create(TIMEOUT)
                    
                    set source[time] = sourceId
                    set affected[time] = entering
                    
                    set affectedLevel[time] = 0
                    
                    set affecting[sourceId][entering] = time
                    
                    set auraCount[entering] = auraCount[entering] + 1
                    set auraInstance[sourceId] = auraInstance[sourceId] + 1
                    
                    set wasActive = false
                else
                    set wasActive = active[time]
                endif
                
                set lev = level[sourceId]
                set active[time] = time.isActive(entering, sourceId)
                
                if (active[time]) then
                    if (auraLevel[entering] < lev) then
                        static if thistype.addBuff.exists then
                            if (0 < buffCount[entering]) then
                                call removeBuff(GetUnitById(entering), auraLevel[entering])
                            endif
                        endif
                        
                        static if thistype.addBuff.exists then
                            call addBuff(GetUnitById(entering), lev)
                        endif
                    endif
                    
                    if (not wasActive) then
                        set buffCount[entering] = buffCount[entering] + 1
                        set activeAura[sourceId] = activeAura[sourceId] + 1
                    endif
                    
                    static if thistype.onEndEffect.exists then
                        if (wasActive) then
                            call time.onEndEffect(sourceId, entering, affectedLevel[time])
                        else
                            static if not thistype.STACKS then
                                if (0 != sourceAffected[entering]) then
                                    set buffCount[entering] = buffCount[entering] - 1
                                    set activeAura[source[sourceAffected[entering]]] = activeAura[source[sourceAffected[entering]]] - 1
                                    set active[sourceAffected[entering]] = false
                                    call thistype(sourceAffected[entering]).onEndEffect(source[sourceAffected[entering]], entering, auraLevel[entering])
                                endif
                            endif
                        endif
                    endif
                    
                    set auraLevel[entering] = lev
                    
                    set sourceAffected[entering] = time
                    static if thistype.onEffect.exists then
                        call time.onEffect(sourceId, entering, lev)
                    endif
                elseif (wasActive) then
                    if (sourceAffected[entering] == time) then
                        set auraLevel[entering] = 0
                        set sourceAffected[entering] = 0
                    endif
                    
                    set buffCount[entering] = buffCount[entering] - 1
                    set activeAura[sourceId] = activeAura[sourceId] - 1
                    
                    static if thistype.addBuff.exists then
                        if (0 == buffCount[entering]) then
                            call removeBuff(GetUnitById(entering), auraLevel[entering])
                        endif
                    endif
                    static if thistype.onEndEffect.exists then
                        call time.onEndEffect(sourceId, entering, affectedLevel[time])
                    endif
                endif
                
                set affectedLevel[time] = lev
            endif
        endmethod
        
        private static method onEnter takes nothing returns boolean
            call doEnter(GetEventSourceUnitId(), GetUnitUserData(GetTriggerUnit()))
            return false
        endmethod
        private static method registerRange takes integer sourceId, unit sourceUnit, real range returns nothing
            set onEnterT[sourceId] = RegisterUnitInRangeEvent(function thistype.onEnter, sourceUnit, range)
        endmethod
        private static method checkLevel takes nothing returns boolean
            local thistype sourceId = next[0]
            local integer newLevel
            
            loop
                exitwhen 0 == sourceId
                
                set newLevel = getLevel(sourceId)
                
                if (newLevel != level[sourceId]) then
                    if (0 != range[sourceId]) then
                        call UnregisterUnitInRangeEvent(onEnterT[sourceId])
                    endif
                    
                    set level[sourceId] = newLevel
                    
                    if (0 == newLevel) then
                        set range[sourceId] = 0
                    else
                        set range[sourceId] = getRange(sourceId, newLevel)
                    endif
                    
                    static if thistype.onLevel.exists then
                        call onLevel(sourceId, level[sourceId])
                    endif
                    
                    if (0 != newLevel) then
                        call doEnter(sourceId, sourceId)
                    endif
                    
                    if (0 != range[sourceId]) then
                        call registerRange(sourceId, GetUnitById(sourceId), range[sourceId])
                    endif
                endif
                
                set sourceId = next[sourceId]
            endloop
            
            return false
        endmethod
        private static method addCheck takes UnitIndex sourceId returns nothing
            call sourceId.lock()
            
            set prev[sourceId]=prev[0]
            set next[sourceId]=0
            set next[prev[0]]=sourceId
            set prev[0]=sourceId
        endmethod
        
        static method createAura takes UnitIndex sourceId returns boolean
            local integer newLevel = getLevel(sourceId)
            local unit sourceUnit = GetUnitById(sourceId)
            
            if (not (IsUnitDead(sourceId) or IsUnitReincarnating(sourceId)) and 0 != GetUnitTypeId(sourceUnit) and GetUnitUserData(sourceUnit) == sourceId) then
                call addCheck(sourceId)
                if (0 == newLevel) then
                    set range[sourceId] = 0
                else
                    set range[sourceId] = getRange(sourceId, newLevel)
                endif
                if (newLevel != level[sourceId]) then
                    static if thistype.onLevel.exists then
                        call onLevel(sourceId, newLevel)
                    endif
                    if (0 != newLevel) then
                        call doEnter(sourceId, sourceId)
                    endif
                    if (0 != range[sourceId]) then
                        call registerRange(sourceId, sourceUnit, range[sourceId])
                    endif
                endif
                
                set level[sourceId] = newLevel
            endif
            
            set sourceUnit = null
            
            return false
        endmethod
        
        private static method onIndex takes nothing returns boolean
            set affecting[GetIndexedUnitId()] = Table.create()
            set level[GetIndexedUnitId()] = 0
            set range[GetIndexedUnitId()] = 0
            set onEnterT[GetIndexedUnitId()] = 0
            set sourceAffected[GetIndexedUnitId()] = 0
            return false
        endmethod
        
        private static method onDeath takes nothing returns boolean
            local integer id = GetUnitUserData(GetTriggerUnit())
            if (0 != level[id]) then
                if (0 != range[id]) then
                    call UnregisterUnitInRangeEvent(onEnterT[id])
                endif
                call removeCheck(id)
                set level[id] = 0
            endif
            
            return false
        endmethod
        
        private static method onRevive takes nothing returns boolean
            set level[GetEventUnitId()] = getLevel(GetEventUnitId())
            if (0 != level[GetEventUnitId()]) then
                set range[GetEventUnitId()] = getRange(GetEventUnitId(), level[GetEventUnitId()])
                call addCheck(GetEventUnitId())
                call doEnter(GetEventUnitId(), GetEventUnitId())
                if (0 != range[GetEventUnitId()]) then
                    call registerRange(GetEventUnitId(), GetEventUnit(), getRange(GetEventUnitId(), level[GetEventUnitId()]))
                endif
            endif
            
            return false
        endmethod
        
        private static method onDeindex takes nothing returns boolean
            if (0 != level[GetIndexedUnitId()]) then
                set level[GetIndexedUnitId()] = 0
                call removeCheck(GetIndexedUnitId())
                call affecting[GetIndexedUnitId()].destroy()
                set affecting[GetIndexedUnitId()] = 0
            endif
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            call Timer[TIMEOUT].list.register(Condition(function thistype.checkLevel))
            
            static if thistype.ANIMATED_BESTOW_AURA then
                call InitModule(function thistype.onIndex, function thistype.onDeindex, function thistype.onDeath, function thistype.onRevive, true)
            else
                call InitModule(function thistype.onIndex, function thistype.onDeindex, function thistype.onDeath, function thistype.onRevive, false)
            endif
        endmethod
    endmodule
endlibrary


Demo
JASS:

struct tester extends array
    //required
    private static constant real TIMEOUT = .3
    private static constant boolean ANIMATED_BESTOW_AURA = true
    private static constant boolean STACKS = false
    
    private static method getLevel takes UnitIndex sourceId returns integer
    endmethod
    
    private static method getRange takes UnitIndex source, integer level returns real
    endmethod
    
    //optional
    private static method removeBuff takes unit whichUnit, integer level returns nothing
        call UnitRemoveAbility(whichUnit, BUFF_ID) //for level?
    endmethod
    
    private static method addBuff takes unit whichUnit, integer level returns nothing
        call DummyCaster[ABILITY_BUFF_ORDER].castTarget(...)
    endmethod
    
    private static method onLevel takes UnitIndex source, integer level returns nothing
    endmethod
    
    private method onEndEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
    endmethod
    
    private method onEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
    endmethod
    
    private method onPeriodicEffect takes UnitIndex source, UnitIndex affected, integer level returns nothing
    endmethod
    
    private static method absFilter takes UnitIndex source, UnitIndex entering returns boolean
    endmethod
    
    private method filter takes UnitIndex source, UnitIndex affected, integer level returns boolean
    endmethod
    
    //required
    implement AuraStruct
    
    //code

    private static method onInit takes nothing returns nothing
        //event registration
    endmethod
endstruct
 

Attachments

  • AuraStruct.w3x
    52 KB · Views: 372
General chit-chat
Help Users
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top