• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

LightweightAura 1.2.0

This library is meant as a direct competitor to Anachron's CustomAura library, which can also be found in the Spells section.

This library allows you to disable or destroy instances when the unit its locked on (the "origin" unit) dies, and re-enable the instance when it gets resurrected.
Instances get forcefully destroyed when the unit its locked on leaves the game (gets removed).
It also allows you to delay the removal of the aura from units that leave the area of the aura.

This library uses vJass, GroupUtils, TimerUtils, Table AutoIndex and AutoEvents.

13/06/2010 - Version 1.2.0
- initial public release


JASS:
// *************************************************************
// *            LightweightAura -- Version 1.2.0
// *                        by Deaod
// *************************************************************
// *
// *    CREDITS:
// *        - grim001 (AutoIndex, AutoEvents)
// *        - Rising_Dusk (GroupUtils)
// *        - Vexorian (JassHelper, Table, TimerUtils)
// *        - MindWorX and PitzerMike (JassNewGenPack)
// *        - Pipedream (Grimoire)
// *        - SFilip (TESH)
// *
// *************************************************************
library LightweightAura requires GroupUtils, TimerUtils, Table, AutoIndex, AutoEvents
    
/**
 *  
 *  The following interface is only a description of the struct "Aura".
 *  It is not actually declared anywhere.
 *  
 *  interface Aura
 *      // destroys this aura after waiting auraPersistenceTime seconds. When destroyed,
 *      // the onRemoveFunc will be called for all remaining units affected by the aura.
 *      // while waiting for auraPersistenceTime seconds, the "destroyed" property of the Aura is true.
 *      public          method              destroy                 takes nothing               returns nothing
 *      
 *      // creates a new Aura instance which follows the origin unit around
 *      public  static  method              create                  takes unit origin, real range returns thistype
 *      
 *      // creates a new Aura instance which has a constant origin
 *      public  static  method              createLoc               takes real x, real y, real range returns thistype
 *      
 *      // when extending the class Aura, you probably want to implement this method.
 *      // it should return true when u should be affected by the aura and false if u should not be affected by the aura.
 *      // this is an interface method. You dont HAVE to implement it.
 *                      method              TargetFilter            takes unit u                returns boolean         defaults true
 *      
 *      // when extending the class Aura, you probably want to implement this method.
 *      // it is run whenever the aura is refreshed.
 *      // this is an interface method. You dont HAVE to implement it.
 *                      method              OnPeriodic              takes nothing               returns nothing         defaults nothing
 *      
 *      // when extending the class Aura, you probably want to implement this method.
 *      // it is run whenever at least one unit is added to the aura.
 *      // this is an interface method. You dont HAVE to implement it.
 *                      method              OnAdd                   takes group targets         returns nothing         defaults nothing
 *      
 *      // when extending the class Aura, you probably want to implement this method.
 *      // it is run whenever at least one unit is removed from the aura.
 *      // this is an interface method. You dont HAVE to implement it.
 *                      method              OnRemove                takes group targets         returns nothing         defaults nothing
 *      
 *      // this gets the current coordinates of the origin of the aura.
 *      // returns the position of the origin unit when periodicFunc was last run.
 *      public          method  operator    originX                 takes nothing               returns real
 *      public          method  operator    originY                 takes nothing               returns real
 *      
 *      // sets the origin of the aura to a constant point. Unbinds the aura from the current origin unit, if any.
 *      public          method              setOriginPos            takes real x, real y        returns nothing
 *      
 *      // gets or sets the origin unit of the aura.
 *      // If you set the origin to null, the position of the previous origin when the periodicFunc was last run will be used.
 *      public          method  operator    origin                  takes nothing               returns unit
 *      public          method  operator    origin=                 takes unit new              returns nothing
 *      
 *      // returns a group of all currently affected units.
 *      // avoid destroying/recycling this group at all costs. It will break the instance.
 *      public          method  operator    targets                 takes nothing               returns group
 *      
 *      // returns a group of all units currently outside the area the aura affects,
 *      // but still being affected because of auraPersistenceTime.
 *      // avoid destroying/recycling this group at all costs. It will break the instance.
 *      public          method  operator    toBeRemoved             takes nothing               returns group
 *      
 *      // the range of the aura.
 *      public          method  operator    range                   takes nothing               returns real
 *      public          method  operator    range=                  takes real new              returns nothing
 *      
 *      // the interval between searching for new units/removing units out of range.
 *      // default is 1./4.
 *      public          method  operator    period                  takes nothing               returns real
 *      public          method  operator    period=                 takes real new              returns nothing
 *      
 *      // the time units have to be outside the range of the aura to be removed from its affected units.
 *      // default is 0.
 *      public          method  operator    auraPersistenceTime     takes nothing               returns real
 *      public          method  operator    auraPersistenceTime=    takes real new              returns nothing
 *      
 *      // returns true only when auraPersistenceTime is greater than 0 and you previously called destroy.
 *      // returns false otherwise
 *      public          method  operator    destroyed               takes nothing               returns boolean
 *      
 *      // a disabled aura will not search for units in its range and will remove all units from its targets group
 *      // after auraPersistenceTime seconds
 *      // default is true.
 *      public          method  operator    enabled                 takes nothing               returns boolean
 *      public          method  operator    enabled=                takes boolean new           returns nothing
 *      
 *      // whether or not to destroy this instance when the origin unit dies.
 *      // default is true.
 *      public          method  operator    destroyOnDeath          takes nothing               returns boolean
 *      public          method  operator    destroyOnDeath=         takes boolean new           returns nothing
 *      
 *      // whether or not to disable this instance when the origin unit dies.
 *      // destroyOnDeath takes precedence over this.
 *      // default is false.
 *      public          method  operator    disableOnDeath          takes nothing               returns boolean
 *      public          method  operator    disableOnDeath=         takes boolean new           returns nothing
 *      
 *      // whether or not to enable this instance again when the origin unit is resurrected.
 *      // default is false.
 *      public          method  operator    enableOnResurrect       takes nothing               returns boolean
 *      public          method  operator    enableOnResurrect=      takes boolean new           returns nothing
 *  endinterface
 */
    
    globals
        private constant    real                DEFAULT_PERIOD              = 1./4
    endglobals
    
    // DO NOT TOUCH ANYTHING BELOW!
    // unless of course you know what you're doing.
    
    interface AuraInterface
        method TargetFilter takes unit target returns boolean defaults true
        method OnPeriodic takes nothing returns nothing defaults nothing
        method OnAdd takes group targets returns nothing defaults nothing
        method OnRemove takes group targets returns nothing defaults nothing
    endinterface
    
    private struct DelayedAuraLoss
        group targets
        Aura a
    endstruct
    
    struct Aura extends AuraInterface
        // sets where the origin of the aura is.
        private real OriginX
        private real OriginY
        private unit Origin
        
        // holds the current targets of the Aura, ie. the units under its influence
        private group Targets
        
        // holds the current targets of the Aura that are scheduled for removal from it.
        private group ToBeRemoved
        
        // Range settings, can be overwritten at runtime.
        private real Range
        
        // timer to base this orb off of.
        private timer T
        private real Period                         = DEFAULT_PERIOD // can be changed at runtime.
        
        // how long the Aura will still affect the targets after they left the range
        private real AuraPersistenceTime            = 0
        private boolean Destroyed                   = false
        private boolean Enabled                     = true
        
        private boolean DestroyOnDeath              = true // destroys this instance when the origin unit dies.
        private boolean DisableOnDeath              = false // disables this instance when the origin unit dies.
        private boolean EnableOnResurrect           = false // re-enables this instance when the origin unit resurrects. DestroyOnDeath must be false for this to work.
        
        private integer TablePos
        
        // STATICS
        private static Table UnitAuraTable
        private static integer array UnitAuraCount
        
        private static thistype tmpd // temporary data, used for group filtering
        private static group tmpg // temporary group
        
        private static method DelayedGroupLostFunc takes nothing returns nothing
        local unit u=GetEnumUnit()
            if IsUnitInGroup(u, tmpd.targets) then
                call GroupRemoveUnit(tmpd.toBeRemoved, u)
                call GroupRemoveUnit(tmpg, u)
            endif
            set u=null
        endmethod
        
        private static method DelayedAuraLost takes nothing returns nothing
        local DelayedAuraLoss dal=DelayedAuraLoss(GetTimerData(GetExpiredTimer()))
            set tmpd=dal.a
            set tmpg=dal.targets
            call ForGroup(dal.targets, function thistype.DelayedGroupLostFunc)
            call dal.a.OnRemove(dal.targets)
            call ReleaseGroup(dal.targets)
            call ReleaseTimer(GetExpiredTimer())
        endmethod
        
        private static method GroupAddFilter takes nothing returns boolean
        local unit u=GetFilterUnit()
            if not IsUnitInGroup(u, tmpd.targets) and (tmpd.TargetFilter(u)) then
                call GroupAddUnit(tmpd.targets, u)
                if IsUnitInGroup(u, tmpd.toBeRemoved) then
                    call GroupRemoveUnit(tmpd.toBeRemoved, u)
                    set u=null
                    return false
                else
                    set u=null
                    return true
                endif
            endif
            set u=null
            return false
        endmethod
        
        private static method GroupLostFunc takes nothing returns nothing
        local unit u=GetEnumUnit()
        local real dx=GetUnitX(u)-tmpd.originX
        local real dy=GetUnitY(u)-tmpd.originY
            if dx*dx+dy*dy>tmpd.range*tmpd.range then
                call GroupRemoveUnit(tmpd.targets, u)
                if tmpd.auraPersistenceTime>0 then
                    call GroupAddUnit(tmpd.toBeRemoved, u)
                endif
                call GroupAddUnit(tmpg, u)
            endif
            set u=null
        endmethod
        
        private static method Callback takes nothing returns nothing
        local thistype s=thistype(GetTimerData(GetExpiredTimer()))
        local group newtargets
        local group losttargets
        // the next two are only used when the user wants the aura to remain on the target for some time
        local timer t
        local DelayedAuraLoss dal
            if not s.destroyed and s.enabled then
                set newtargets=NewGroup()
                set losttargets=NewGroup()
                if s.origin!=null then
                    set s.OriginX=GetUnitX(s.origin)
                    set s.OriginY=GetUnitY(s.origin)
                endif
                set tmpd=s
                set tmpg=losttargets
                call ForGroup(s.targets, function thistype.GroupLostFunc)
                set tmpg=newtargets
                call GroupEnumUnitsInRange(newtargets, s.originX, s.originY, s.range, function thistype.GroupAddFilter)
                // now lets execute the events
                call s.OnPeriodic()
                if FirstOfGroup(losttargets)!=null then // see if losttargets has at least one unit
                    if tmpd.auraPersistenceTime<=0 then // and check if the user wants to delay the removal of units outside the range
                        call s.OnRemove(losttargets)
                        call ReleaseGroup(losttargets)
                    else
                        set dal=DelayedAuraLoss.create()
                        set dal.a=s
                        set dal.targets=losttargets
                        set t=NewTimer()
                        call SetTimerData(t, integer(dal))
                        call TimerStart(t, s.auraPersistenceTime, false, function thistype.DelayedAuraLost)
                    endif
                else
                    call ReleaseGroup(losttargets)
                endif
                if FirstOfGroup(newtargets)!=null then // see if newtargets has at least one unit
                    call s.OnAdd(newtargets)
                endif
                call ReleaseGroup(newtargets)
            else
                call s.OnPeriodic()
            endif
        endmethod
        
        private method DestroyAction takes nothing returns nothing
        local group g
            if FirstOfGroup(targets)!=null then
                set g=NewGroup()
                set bj_groupAddGroupDest=g
                call ForGroup(Targets, function GroupAddGroupEnum)
                call GroupClear(Targets)
                call OnRemove(g)
            endif
            call ReleaseTimer(T)
            call ReleaseGroup(Targets)
            call ReleaseGroup(ToBeRemoved)
            call RemoveFromTable()
            set Origin=null
            
            call deallocate()
        endmethod
        
        private static method DestroyCallback takes nothing returns nothing
        local thistype s=thistype(GetTimerData(GetExpiredTimer()))
            call s.DestroyAction()
            call ReleaseTimer(GetExpiredTimer())
        endmethod
        
        public method destroy takes nothing returns nothing
        local timer t
            if auraPersistenceTime>0 and FirstOfGroup(targets)!=null then
                set t=NewTimer()
                call SetTimerData(t, this)
                call TimerStart(t, auraPersistenceTime, false, function thistype.DestroyCallback)
                set Destroyed=true
            else
                call DestroyAction()
            endif
        endmethod
        
        private method AddToTable takes nothing returns nothing
        local integer id=GetUnitId(origin)
            if id>0 then
                set UnitAuraTable[id*8192+UnitAuraCount[id]]=this
                set TablePos=UnitAuraCount[id]
                set UnitAuraCount[id]=UnitAuraCount[id]+1
            endif
        endmethod
        private method RemoveFromTable takes nothing returns nothing
        local integer id=GetUnitId(origin)
            if id>0 then
                set UnitAuraCount[id]=UnitAuraCount[id]-1
                set UnitAuraTable[id*8192+TablePos]=UnitAuraTable[id*8192+UnitAuraCount[id]]
                set thistype(UnitAuraTable[id*8192+TablePos]).TablePos=TablePos
            endif
        endmethod
        
        public static method create takes unit origin, real range returns thistype
        local thistype s=allocate()
            set s.origin=origin
            set s.range=range
            set s.Targets=NewGroup()
            set s.ToBeRemoved=NewGroup()
            set s.T=NewTimer()
            call SetTimerData(s.T, integer(s))
            call TimerStart(s.T, s.period, true, function thistype.Callback)
            call s.AddToTable()
            return s
        endmethod
        
        public static method createLoc takes real x, real y, real range returns thistype
            local thistype s=allocate()
            set s.origin=null
            call s.setOriginPos(x, y)
            set s.range=range
            set s.Targets=NewGroup()
            set s.ToBeRemoved=NewGroup()
            set s.T=NewTimer()
            call SetTimerData(s.T, integer(s))
            call TimerStart(s.T, s.period, true, function thistype.Callback)
            return s
        endmethod
        
        public method operator originX takes nothing returns real
            return OriginX
        endmethod
        public method operator originY takes nothing returns real
            return OriginY
        endmethod
        public method setOriginPos takes real x, real y returns nothing
            set OriginX=x
            set OriginY=y
            call RemoveFromTable()
            set Origin=null
        endmethod
        
        public method operator origin takes nothing returns unit
            return Origin
        endmethod
        public method operator origin= takes unit new returns nothing
            call RemoveFromTable()
            set Origin=new
            call AddToTable()
        endmethod
        
        public method operator targets takes nothing returns group
            return Targets
        endmethod
        
        public method operator toBeRemoved takes nothing returns group
            return ToBeRemoved
        endmethod
        
        public method operator range takes nothing returns real
            return Range
        endmethod
        public method operator range= takes real new returns nothing
            set Range=new
        endmethod
        
        public method operator period takes nothing returns real
            return Period
        endmethod
        public method operator period= takes real new returns nothing
            set Period=new
            call TimerStart(T, Period, true, function thistype.Callback)
        endmethod
        
        public method operator auraPersistenceTime takes nothing returns real
            return AuraPersistenceTime
        endmethod
        public method operator auraPersistenceTime= takes real new returns nothing
            set AuraPersistenceTime=new
        endmethod
        
        public method operator destroyed takes nothing returns boolean
            return Destroyed
        endmethod
        
        private method DisableAction takes nothing returns nothing
        local group g
            set g=NewGroup()
            set bj_groupAddGroupDest=g
            call ForGroup(targets, function GroupAddGroupEnum)
            call GroupClear(targets)
            call OnRemove(g)
        endmethod
        
        private static method DisableCallback takes nothing returns nothing
        local thistype s=thistype(GetTimerData(GetExpiredTimer()))
            call s.DisableAction()
            call ReleaseTimer(GetExpiredTimer())
        endmethod
        
        public method operator enabled takes nothing returns boolean
            return Enabled
        endmethod
        public method operator enabled= takes boolean new returns nothing
        local timer t
            if new==false and enabled==true and FirstOfGroup(targets)!=null then
                // just got disabled and is affecting at least one unit
                if auraPersistenceTime>0 then
                    set t=NewTimer()
                    call SetTimerData(t, this)
                    call TimerStart(t, auraPersistenceTime, false, function thistype.DisableCallback)
                else
                    call DisableAction()
                endif
            endif
            set Enabled=new
        endmethod
        
        public method operator destroyOnDeath takes nothing returns boolean
            return DestroyOnDeath
        endmethod
        public method operator destroyOnDeath= takes boolean new returns nothing
            set DestroyOnDeath=new
        endmethod
        
        public method operator disableOnDeath takes nothing returns boolean
            return DisableOnDeath
        endmethod
        public method operator disableOnDeath= takes boolean new returns nothing
            set DisableOnDeath=new
        endmethod
        
        public method operator enableOnResurrect takes nothing returns boolean
            return EnableOnResurrect
        endmethod
        public method operator enableOnResurrect= takes boolean new returns nothing
            set EnableOnResurrect=new
        endmethod
        
        private static method OnRemoved takes unit u returns nothing
        local integer id=GetUnitId(u)
        local integer i=UnitAuraCount[id]-1
            loop
                exitwhen i<0
                call thistype(UnitAuraTable[id*8192+i]).destroy()
                set i=i-1
            endloop
        endmethod
        
        private static method OnDeath takes nothing returns boolean
        local integer id=GetUnitId(GetTriggerUnit())
        local integer i=UnitAuraCount[id]-1
        local thistype s
            loop
                exitwhen i<0
                set s=thistype(UnitAuraTable[id*8192+i])
                if s.destroyOnDeath then
                    call s.destroy()
                elseif s.disableOnDeath then
                    set s.enabled=false
                endif
                set i=i-1
            endloop
            return false
        endmethod
        
        private static method OnResurrect takes unit u returns nothing
        local integer id=GetUnitId(u)
        local integer i=UnitAuraCount[id]-1
        local thistype s
            loop
                exitwhen i<0
                set s=thistype(UnitAuraTable[id*8192+i])
                if s.enableOnResurrect then
                    set s.enabled=true
                endif
                set i=i-1
            endloop
        endmethod
        
        private static method OnRevive takes nothing returns boolean
            call OnResurrect(GetTriggerUnit())
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
        local trigger t
            set UnitAuraTable=Table.create()
            
            call OnUnitDeindexed(thistype.OnRemoved)
            call OnUnitResurrect(thistype.OnResurrect)
            
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function thistype.OnDeath))
            
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_REVIVE_FINISH)
            call TriggerAddCondition(t, Condition(function thistype.OnRevive))
        endmethod
    endstruct
    
endlibrary

Keywords:
Aura, Lightweight
Contents

Just another Warcraft III map (Map)

Reviews
16:41, 13th Jun 2010 TriggerHappy: I still personally believe some of your operators are nonsense. Nevertheless, it seems to have more features than the other aura system approved here. Easy to use as well.

Moderator

M

Moderator

16:41, 13th Jun 2010
TriggerHappy:

I still personally believe some of your operators are nonsense.
Nevertheless, it seems to have more features than the other aura system approved here.
Easy to use as well.
 
Level 1
Joined
Aug 9, 2009
Messages
275
Seems nice. Tho there are some grammar mistakes :). You write Its (It's), which mean it is, when it's supposed to be is.

Anyway, the description is nice, might go test laters.
 
Level 15
Joined
Jul 6, 2009
Messages
889
If you can't grasp UAC's basic concept. How do you think you'll be able to work something similiar in concept. Although I haven't tried any of the three aura systems.

I'd use whatever is easier for you. No one is really telling you which to use. It's your choice. (Hm. Will there be "This system is more efficient than ..." arguments ;)?)

Either way....
 
Level 14
Joined
Nov 18, 2007
Messages
816
Will there be "This system is more efficient than ..." arguments ;)?
I can try:

All libraries use interfaces to allow the user to run his code when something related to the aura happens (a unit enters the range of the aura/leaves it, ...).

UAC and LightweightAura use a "real" type value, which the user can change, for the range of the Aura, CustomAura currently uses an interface method for that. It works, but is much slower.

CustomAura allows you to run code when the instances is first processed and when it is destroyed.

LightweightAura allows you to set the origin of an aura to a specific constant point.

UAC and CustomAura use a static period for all instances which can only be modified at compile time (the default for UAC is 1/32 of a second, the default for CustomAura is 0.0375). LightweightAura uses a dynamic (adjustable at runtime) period (default is 1/4 of a second). While not as fast with many instances (more than 1?), the default is less straining on the performance.

Also, CustomAura and UAC both offer events on a per unit basis. LightweightAura only offers the target filter on a per unit basis, the rest of its events use groups, which is faster when more than one unit enters the aura at the same time.

LightweightAura is currently the only system (of those three) that allows you to delay the removal of units from those affected by the aura.

UAC and LightweightAura offer destroying/disabling the aura when the unit its attached to dies. Both also offer enabling the aura again, after the unit revived. UAC's revival detection only works on heroes. LightweightAura's works on all forms of revival currently detectable.

UAC allows you to run code when the unit an aura is attached to dies or resurrects.

All three allow you to run code on the Aura periodically. UAC has made the interval the code is run in adjustable, although only multiples of its PERIOD will work (all others will get rounded down, or up to PERIOD). LightweightAura will run the code whenever the instances period hits. CustomAura will run the code whenever its period hits.

UAC doesnt have any dependencies, CustomAura only requires GroupUtils, while LightweightAura requires GroupUtils, TimerUtils, Table, AutoIndex and AutoEvents.

Conclusion: I think its pretty much a tie between UAC and LightweightAura (with CustomAura being far off). Use LightweightAura if you need one of its features (ironically, LightweightAura has the most features), or use UAC if you want to avoid importing other libraries or need to run code when the unit an aura is attached to dies. Otherwise, i couldnt care less which one you use.

Personally, i'd prefer LightweightAura, not only because i wrote it, but because UAC does some things i consider bad (like not using GroupUtils to recycle groups and instead using a custom algorithm, or using a hashtable instance, when all you need is a Table instance, though it does save you an integer multiplication and addition).

Dont let my personal preference influence you, see for yourself which one suits you better.
 
Level 14
Joined
Nov 18, 2007
Messages
816
A Table is a struct instance from Vexorians Table library. On the backend its a hashtable parent (a part of a hashtable), not a whole hashtable. I mentioned the integer multiplication and addition because thats the additional time cost of using a Table instead of a hashtable.

Do you think you need to care about a multiplication and addition of integers?
 
UAC and CustomAura use a static period for all instances which can only be modified at compile time (the default for UAC is 1/32 of a second, the default for CustomAura is 0.0375). LightweightAura uses a dynamic (adjustable at runtime) period (default is 1/4 of a second). While not as fast with many instances (more than 1?), the default is less straining on the performance.
Nobody wants to change the aura interval ingame.
 
Top