• 🏆 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!

[JASS] LightweightAura

Status
Not open for further replies.
Level 14
Joined
Nov 18, 2007
Messages
816
Alright, this is meant as a preview of an upcoming library of mine. It is a direct competitor to Anachron's CustomAura library, which can be found in the Spells section.

It doesnt come with any additional libraries.
You dont have to extend the struct provided by this library (you can however, should you find the need for additional features). You use function interfaces to respond to the various events this library features.
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 and AutoIndex (currently).
Once wc3c.net is back online, i will finish this library by using AutoEvents to detect resurrection. It already has support for hero revival.

JASS:
// *************************************************************
// *            LightweightAura -- Version 1.0.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
    
/**
 *  
 *  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, AuraFilter filterFunc, AuraOnAdd onAddFunc, AuraOnRemove onRemoveFunc returns thistype
 *      
 *      // creates a new Aura instance which has a constant origin
 *      public  static  method              createLoc               takes real x, real y, real range, AuraFilter filterFunc, AuraOnAdd onAddFunc, AuraOnRemove onRemoveFunc returns thistype
 *      
 *      // this property is a function interface type. The function is executed every time a unit enters the range of the aura.
 *      public          method  operator    filterFunc              takes nothing               returns AuraFilter
 *      public          method  operator    filterFunc=             takes AuraFilter func       returns nothing
 *      
 *      function interface AuraFilter takes Aura a, unit target returns boolean
 *      
 *      
 *      // this function interface type is executed every time the aura is "refreshed", ie. new units are added to group and units out of range are removed.
 *      public          method  operator    periodicFunc            takes nothing               returns AuraPeriodic
 *      public          method  operator    periodicFunc=           takes AuraPeriodic func     returns nothing
 *      
 *      function interface AuraPeriodic takes Aura a returns nothing
 *      
 *      
 *      // this function interface type is executed whenever at least one unit is added to the affected units.
 *      // The group passed to it contains all new units affected.
 *      // You must release the group passed to it yourself (you have to use GroupUtils).
 *      public          method  operator    onAddFunc               takes nothing               returns AuraOnAdd
 *      public          method  operator    onAddFunc=              takes AuraOnAdd func        returns nothing
 *      
 *      function interface AuraOnAdd takes Aura a, group targets returns nothing
 *      
 *      
 *      // this function interface type is executed whenever at least one unit is removed from the affected units.
 *      // The group passed to it contains all removed units that were previously affected.
 *      // You must release the group passed to it yourself (you have to use GroupUtils).
 *      public          method  operator    onRemoveFunc            takes nothing               returns AuraOnRemove
 *      public          method  operator    onRemoveFunc=           takes AuraOnRemove func     returns nothing
 *      
 *      function interface AuraOnRemove takes Aura a, group targets returns 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
 *      
 *      // 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.
    
    function interface AuraFilter takes Aura a, unit target returns boolean
    function interface AuraPeriodic takes Aura a returns nothing
    function interface AuraOnAdd takes Aura a, group targets returns nothing
    function interface AuraOnRemove takes Aura a, group targets returns nothing
    
    private struct DelayedAuraLoss
        group targets
        Aura a
    endstruct
    
    struct Aura
        // Event Callbacks, may be registered individually.
        private AuraFilter FilterFunc               = 0 // filters each unit entering the Aura individually.
        private AuraPeriodic PeriodicFunc           = 0 // run every time the aura is refreshed.
        private AuraOnAdd OnAddFunc                 = 0 // run when at least one new unit is added to the targets of the aura.
        private AuraOnRemove OnRemoveFunc           = 0 // run when at least one unit is removed from the targets of the aura.
        
        // sets where the origin of the aura is.
        private real OriginX
        private real OriginY
        private unit Origin
        private boolean OriginUnit                  = true // if true, the origin will always be at the center of the unit, if false, it will always be at the given x and y coordinates.
        
        // holds the current targets of the Aura, ie. the units under its influence
        private group Targets
        
        // 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 DelayedAuraLost takes nothing returns nothing
        local DelayedAuraLoss dal=DelayedAuraLoss(GetTimerData(GetExpiredTimer()))
            set bj_groupRemoveGroupDest=dal.a.Targets
            call ForGroup(dal.targets, function GroupRemoveGroupEnum)
            if dal.a.onRemoveFunc>0 then
                call dal.a.onRemoveFunc.evaluate(dal.a, dal.targets)
            endif
            call ReleaseTimer(GetExpiredTimer())
        endmethod
        
        private static method GroupAddFilter takes nothing returns boolean
        local unit u=GetFilterUnit()
            if not IsUnitInGroup(u, tmpd.targets) and (tmpd.filterFunc==0 or tmpd.filterFunc.evaluate(tmpd, u)) then
                call GroupAddUnit(tmpd.targets, u)
                set u=null
                return true
            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
                if tmpd.auraPersistenceTime<=0 then
                    call GroupRemoveUnit(tmpd.Targets, 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=NewGroup()
        local group losttargets=NewGroup()
        // 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
                if s.OriginUnit 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
                if s.periodicFunc>0 then
                    call s.periodicFunc.evaluate(s)
                endif
                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
                        if s.onRemoveFunc>0 then
                            call s.onRemoveFunc.evaluate(s, losttargets)
                        endif
                    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 and s.onAddFunc>0 then // see if newtargets has at least one unit
                    call s.onAddFunc.evaluate(s, newtargets)
                else
                    call ReleaseGroup(newtargets)
                endif
            elseif s.periodicFunc>0 then
                call s.periodicFunc.evaluate(s)
            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)
                if onRemoveFunc>0 then
                    call onRemoveFunc.evaluate(this, g)
                endif
            endif
            call ReleaseTimer(T)
            call ReleaseGroup(Targets)
            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, AuraFilter filterFunc, AuraOnAdd onAddFunc, AuraOnRemove onRemoveFunc returns thistype
        local thistype s=allocate()
            set s.origin=origin
            set s.range=range
            set s.filterFunc=filterFunc
            set s.onAddFunc=onAddFunc
            set s.onRemoveFunc=onRemoveFunc
            set s.Targets=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, AuraFilter filterFunc, AuraOnAdd onAddFunc, AuraOnRemove onRemoveFunc returns thistype
            local thistype s=allocate()
            set s.origin=null
            call s.setOriginPos(x, y)
            set s.range=range
            set s.filterFunc=filterFunc
            set s.onAddFunc=onAddFunc
            set s.onRemoveFunc=onRemoveFunc
            set s.Targets=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 filterFunc takes nothing returns AuraFilter
            return FilterFunc
        endmethod
        public method operator filterFunc= takes AuraFilter func returns nothing
            set FilterFunc=func
        endmethod
        
        public method operator periodicFunc takes nothing returns AuraPeriodic
            return PeriodicFunc
        endmethod
        public method operator periodicFunc= takes AuraPeriodic func returns nothing
            set PeriodicFunc=func
        endmethod
        
        public method operator onAddFunc takes nothing returns AuraOnAdd
            return OnAddFunc
        endmethod
        public method operator onAddFunc= takes AuraOnAdd func returns nothing
            set OnAddFunc=func
        endmethod
        
        public method operator onRemoveFunc takes nothing returns AuraOnRemove
            return OnRemoveFunc
        endmethod
        public method operator onRemoveFunc= takes AuraOnRemove func returns nothing
            set OnRemoveFunc=func
        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
            set OriginUnit=false
            call RemoveFromTable()
        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()
            set OriginUnit=new!=null
        endmethod
        
        public method operator targets takes nothing returns group
            return Targets
        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)
            if onRemoveFunc>0 then
                call onRemoveFunc.evaluate(this, g)
            endif
        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 nothing
        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
        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 nothing
            call OnResurrect(GetTriggerUnit())
        endmethod
        
        private static method onInit takes nothing returns nothing
        local trigger t
            set UnitAuraTable=Table.create()
            
            call OnUnitDeindexed(thistype.OnRemoved)
            
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddAction(t, function thistype.OnDeath)
            
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_REVIVE_FINISH)
            call TriggerAddAction(t, function thistype.OnRevive)
        endmethod
    endstruct
    
endlibrary

Again: Note that this is a preview and in no way meant as a final version. If you find something you think would need improvement, dont hesitate to tell me.
 

Attachments

  • LightweightAura_1.0.0.w3x
    62.7 KB · Views: 76
Level 14
Joined
Nov 18, 2007
Messages
816
I dont like exposing variables to the user.
Also, take a look at the operators for Enabled ("enabled" and "enabled="). enabled= does more than just setting a variable, although the API makes it seem as if youre treating a normal variable.

I even thought about returning a copy of Targets whenever a user tries to access targets. But that would have lead to some serious performance issues.
 
Deaod said:
Also, take a look at the operators for Enabled ("enabled" and "enabled="). enabled= does more than just setting a variable, although the API makes it seem as if youre treating a normal variable.

Yes, I understand that. I obviously wasn't talking about operators like that.

Deaod said:
I dont like exposing variables to the user.

So you'd rather replace a faster variable setting with a function call?
Unless operators get inlined too, but even then it just looks silly to me.
 
Level 6
Joined
Jun 30, 2006
Messages
230
Cool system, but we don't really need another one I think.

Of course you would think that. I haven't tried either system, but I remember trying to make custom auras in the past. It was a nightmare (honestly, partly because I hadn't really learned JASS yet). I could have used these systems then :)
 
Status
Not open for further replies.
Top