System UAC - Unit Aura Control

Discussion in 'Systems and Snippets' started by Romek, Nov 25, 2008.

  1. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    Unit Aura Control
    By Romek
    Version 3.1
    Introduction:
    UAC is a system that was originally designed to make creating Aura-type spells much easier than it currently is, although it can be used for anything which requires "Unit In Range" type events. See the documentation (in the map) and the example spell for more information!

    The Documentation:
    JASS:
    .
       _   _  _   ___                                         
      | | | |/_\ / __|     U N I T   A U R A   C O N T R O L                                  Last Update: 22nd Nov 2009
      | |_| / _ \ (__             B y   R o m e k
       \___/_/ \_\___|              Version 3.1
    
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        Contact Information:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
            My Profile:
              [url]www.thehelper.net/forums/member.php?u=7807[/url]
              
            UAC Thread:
              [url]www.thehelper.net/forums/showthread.php?t=112374[/url]
              
        What is UAC?
        ¯¯¯¯¯¯¯¯¯¯¯¯
            UAC is a system which allows the creation of aura-type abilities in an efficient, user-friendly way.
            Think of it as the aura equivalent of the caster system.
            
            Although UAC alone can be used to create incredible auras very easily, optional modules are also 
            available, which can provide extra options and methods.
            
            UAC requires vJASS knowledge.
            
        How to Implement:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
            - Open your map, open the trigger editor (F4)
            - Create a new trigger object (CTRL + T), and name it whatever you want (preferably UAC).
            - Go to Edit -> Convert to Custom Text
            - Delete all the text that appears
            - Copy everything in the UAC trigger, and paste it into the trigger you created
            - You may want to change the configurable, though this isn't recommended.
            - Save, and enjoy!
            
        How to Use:
        ¯¯¯¯¯¯¯¯¯¯¯
            - Create a new JASS trigger.
            - Create a struct, and make it extend UAC. (struct Name extends UAC)
            - Then you can use all the methods, members and interface methods listed below 
              on (and in) this new struct you created.
            - See the example spell(s) for real usage.
            
        Methods:
        ¯¯¯¯¯¯¯¯
            _______________________________________________________________________
             static method create takes unit mainUnit, real range returns thistype
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This creates an instance for you to use. 'mainUnit' is the unit which
                has the aura spell. 'range' is the range of the aura. Units within this range
                of 'mainUnit' will be affected. See 'main' and 'range' members for more detail.
                If your struct has a custom create method, you will need to call 'allocate' instead
                of create. The arguments will be exactly the same though.
                Remember to use <YourStructName>.create(...), not UAC.create(...).
                
                local Aura data = Aura.create(GetTriggerUnit(), 600.)
                
            ________________________________________________________
             static method get takes unit mainUnit returns thistype
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This returns the aura instance which 'mainUnit' is the main unit for.
                This is most useful when you need to check if a unit leveled up the aura, or learned it, 
                as it'd be bad to create a new instance everytime a unit levels a spell.
                This will return 0 if there is no data.
                This will only return a struct of the type that the method is used from. So if a unit has two
                auras, X and Y; X.get(u) and Y.get(u) will return different structs. Similarly, Z.get(u) will
                return 0.
                
                if Aura.get(u) == 0 then
                    set data = Aura.create(...)
                else
                    call BJDebugMsg("Aura already learned. Levelling up!")
                endif
                
            ________________________________________________________
             method isAffected takes unit whichUnit returns boolean
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method is used to check if 'whichUnit' is affected by the aura.
                It will return true if the unit is, and false if it isn't.
                This will only return true if the unit is affected by the current instance.
                So even if the unit is affected by an aura of the same type as the current instance,
                this'll return false if it's not affected by this exact instance.
                
                This is useful for auras such as 'double damage' auras. It's impossible to give a unit
                double the damage when it enters or leaves the auras range; nor is it possible to do so
                every 0.03125 (by default) seconds. However, if using damage detection, you could check if
                the damage source is affected by the aura. If it is, then deal the damage again.
                
                if data.isAffected(GetEventDamageSource()) then
                    call KillUnit(GetTriggerUnit())
                endif
                
                
        Members:
        ¯¯¯¯¯¯¯¯
            ________________    
             unit main          'main' can be used to retrieve the main unit for the aura. The main unit is the unit
             unit mainUnit      that has the aura ability, and units come within range of this unit to become affected.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This can be used in all the interface methods (more below).
                'main' is a readonly member, meaning it cannot be set. Only retrieved.
                'mainUnit' can also be used instead of 'main'. It does exactly the same thing, just with a different name.
                
                call SetWidgetLife(data.main, 500.)
                call SetWidgetLife(data.mainUnit, 500.)
                
            ________________   
             real range         'range' is the range of the aura. Units that come within this range of the main unit
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    become affected. If this is set to 0 or less, the instance will be paused, and the      
                range will need to be reset to a higher number to resume the aura.
                
                set data.range = data.range + 100
                
            ________________
             boolean pause      'pause' determines whether or not a struct instance is paused. When an instance is
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    paused, no units are affected by the aura, and no effects happen. The struct is 
                virtually destroyed during the time it's paused.
                When a struct is paused, all affected units will become unaffected.
                
                if data.pause then
                    set data.pause = false
                endif
                
            ________________
             boolean single     'single' determines whether or not the struct will be destroyed when the main unit dies.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    If single is true, then the struct will be destroyed when the main unit dies. Otherwise, 
                the struct will be paused until the unit is revived (or it'll just remain paused forever).
                'single' is automatically set to true if the main unit is not a hero. Otherwise, it stays false.
                
                if IsUnitType(u, UNIT_TYPE_HERO) == true then
                    set data.single = false
                endif
                
            ________________
             integer count      'count' simply returns the amount of units that are currently affected by an aura.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This is useful if you want certain effects to happen depending on the amount of units
                within range. For example, divide some damage between all targets, or heal the caster for targets * 100.
                'count' is a readonly member, meaning it cannot be set. Only retrieved.
                
                call SetWidgetLife(data.main, 1000 / data.count)
                
            ________________
             real ticker        'ticker' is, in seconds, how often the periodic functions of UAC are called for the
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    particular instance. This includes the frequency at which units can become affected
                or unaffected, and how often unitPeriodic and timerLoop are called. This is automatically rounded down
                to a multiple of TIMEOUT. This is TIMEOUT by default. 
                
                set data.ticker = data.ticker + TIMEOUT
                
                
        Interface Methods:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
            - All of these methods must be declared in your struct. They aren't used like normal methods are,
              but are simply created within the struct, then automagically called when appropriate.
            - All of these methods are optional.
            - The arguments do not have to be named the same as they are in the codes below.
            ________________________________________________________
             method unitEnter takes unit whichUnit returns nothing
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method is called when a unit comes within range of the mainunit. 'whichUnit' is the unit
                that came within range, and is now affected by the aura.
                
                method unitEnter takes unit u returns nothing
                    call SetUnitMoveSpeed(u, 100.)
                endmethod
                
            ________________________________________________________
             method unitLeave takes unit whichUnit returns nothing
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method goes hand-in-hand with unitEnter. It does the complete opposite.
                This method is called when a unit that was affected by the aura stops being affected.
                'whichUnit' is the unit that left the range of the aura.
                This method is often used to remove any effects that were created in unitEnter.
                
                method unitLeave takes unit u returns nothing
                    call SetUnitMoveSpeed(u, GetUnitDefaultMoveSpeed(u))
                endmethod   
                
            __________________________________________________________
             method unitPeriodic takes unit whichUnit returns nothing
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method is called every 0.03125 (by default) seconds, for every unit that is affected
                by the aura. Useful for continous effects which need to be updated constantly for as long 
                as the unit is affected.
                
                method unitPeriodic takes unit u returns nothing
                    call SetUnitMoveSpeed(u, GetUnitMoveSpeed(u) - 1)
                endmethod
                
            ________________________________________________
             method mainDeath takes nothing returns nothing
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method is called when the main unit dies. Useful for removing any aura effects attached
                to the main unit, or undoing any main-unit only effects. unitLeave is automatically called for
                every unit affected.
                
                method mainDeath takes nothing returns nothing
                    call DestroyEffect(this.effect)
                endmethod
                
            _________________________________________________
             method mainRevive takes nothing returns nothing
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method is called when the main unit is revived. This method will never be called if
                'single' was true. This is useful for recreating anything that was removed in mainDeath.
                All units that would be affected become affected automatically.
                
                method mainRevive takes nothing returns nothing
                    set this.effect = AddSpecialEffectTarget("model", .main, "origin")
                endmethod
                
            ________________________________________________
             method timerLoop takes nothing returns nothing
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                This method is called every 0.03125 seconds (by default). Unlike unitPeriodic, it doesn't take
                a unit argument. It's simply called regardless of any conditions, units, etc.
                Can save you a timer. Useable as a 'unitPeriodic' for the mainUnit only, as unitPeriodic isn't
                suitable for this (it could be called any amount of times, depending on the amount of units affected).
                This method is called only once every 0.03125 seconds.
                
                method timerLoop takes nothing returns nothing
                    call SetUnitPosition(.dummy, GetUnitX(.mainUnit), GetUnitX(.main))
                endmethod
                
                
        Filter Methods:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
             - These are similar to Interface Methods, in the sense that they also have to be declared manually
               in the struct.
             - These are used to prevent unwanted units from being affected by the aura.
             - If these return true, then the unit that was passed is a valid target.
             - All of these are optional.
    
             ________________________________________________________
              method andFilterA takes unit whichUnit returns boolean
              method andFilterB takes unit whichUnit returns boolean
              method andFilterC takes unit whichUnit returns boolean
             ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
             - All of the declared and filters must be true for the unit to be a valid taget
             
             _______________________________________________________
              method orFilterA takes unit whichUnit returns boolean
              method orFilterB takes unit whichUnit returns boolean
              method orFilterC takes unit whichUnit returns boolean
             ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
             - At least one of the declared or filters must be true for the unit to be a valid target.
             
        
        Changelog:
        ¯¯¯¯¯¯¯¯¯¯
            3.1
            - Readded Tickers
        
            3.0
            - Complete rewrite of script. It's now more efficient, stable and virtually limitless. 
            - Code is also littered with comments.
            - Renamed most methods:
                - UnitEnter -> unitEnter
                - UnitLeave -> unitLeave
                - unitPeriodic -> unitPeriodic
                - OrFilter% -> orFilter%
                - AndFilter% -> andFilter%
                - MainDeath -> mainDeath
                - MainRevive -> mainRevive
                - TimerLoop -> timerLoop
                - MainUnit -> main            
            - Added optional use of mainUnit instead of main
            - Some additional features have been removed (such as effects)
            - Added support for optional extensions
                - Created UAC Effects
                - Created UAC Filters
            - Created new readme
            - Added more safety and debugging options
            - pausing now works as it should
            - This should be the last major update. (Enough with the interface changes (sorry!))
            
            2.2                                                                                                        
            - Added the TimerLoop method                                                                               
            - Now works with the new 1.23b patch. (And is more stable as a result!)                                    
                                                                                                                        
            2.1                                                                                                        
            - Fixed a minor coding error in the create method                                                          
            - Added "count" method operator                                                                            
            - Fixed a bug which involved leaking an instance with heroes which had more than one aura                  
            - Lots of changes related to multiple auras per unit.                                                      
            - Added 'effect' and 'attachpoint' operators, for the newly added Effects.                                 
            - TIMEOUT is now public. It can be accessed by using UAC_TIMEOUT.                                          
                                                                                                                        
            2.0b                                                                                                       
            - Removed the limitation of 1 aura ability per unit.                                                       
                                                                                                                        
            2.0                                                                                                        
            - Re-wrote the entire system to meet my standards                                                          
            - Added MainDeath and MainRevive methods                                                                   
            - Changed most of the current methods                                                                      
            - Added internal attachment, so the system no longer depends on PUI                                        
            - Added IsAffected Method                                                                                  
            - Added 'Single' operator                                                                                  
                                                                                                                        
            1.8                                                                                                        
            - Changed the SetRange method to an operator                                                               
            - Implemented Pausing                                                                                      
                                                                                                                        
            1.7                                                                                                        
            - Added Tickers                                                                                            
                                                                                                                        
            1.6                                                                                                        
            - Fixed some small coding errors                                                                           
            - Fixed a bug which continously called "UnitLeave" for the main unit. (Silly me :p)                        
                                                                                                                        
            1.5                                                                                                        
            - Added the .Destroy method. When 'set data.Destroy = true' is used; when the main                         
              unit dies, the struct will be destroyed automatically.                                                   
                                                                                                                        
            1.4                                                                                                        
            - Recoded the entire system to improve on efficiency and fix some sloppy coding.                           
            - The main functions now take 2 arguments. (enumerated unit, and main unit)                                
            - Renamed UnitInRange to UnitPeriodic                                                                      
            - Renamed UnitEntersRange to UnitEnter                                                                     
            - Renamed UnitLeavesRange to UnitLeave                                                                     
            - Filters have been completely revamped. They are now optional functions, and there are 'And' and 'Or'      
              filters. At least one of the 'Or' ones has to be true, and all of the 'And's have to be true.            
              They are all optional.                                                                                   
            - As a result of the filters being remade, the filter library is now removed. (Didn't last long!)          
                                                                                                                        
            1.3                                                                                                        
            - Fixed a bug which occured when the 'main' unit died.                                                     
            - Added a library of common filters, so the user doesn't have to create identical functions                
              for most auras.                                                                                          
            - Fixed a bug which kept calling UnitInRange for units which where enumerated at least                     
              once, but then should've been filtered. (Maybe the unit became flying, etc)                              
              been filtered.                                                                                           
                                                                                                                        
            1.2                                                                                                        
            - Changed another small coding error                                                                       
            - Made TrueGroup non-static to avoid bugs involving units in range of                                      
              multiple 'aura units'                                                                                    
            - Removed a useless variable from a function                                                               
                                                                                                                        
            1.1                                                                                                        
            - Fixed a coding error involving index recycling                                                           
                                                                                                                         
            1.0:                                                                                                       
            - Initial Release

    The Code:
    JASS:
    library UAC
    globals
    //           _______________________________________________________________
    //          +---------------------------------------------------------------+
    //          ||     _   _  _   ___                                          ||
    //          ||    | | | |/_\ / __|     U N I T   A U R A   C O N T R O L   ||
    //          ||    | |_| / _ \ (__             B y   R o m e k              ||
    //          ||     \___/_/ \_\___|              Version 3.1                ||
    //   _______||_____________________________________________________________||_______
    //  ||¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯||
    //  ||  U A C allows users to create aura-type spells in a simple, user-friendly   ||
    //  ||  way, with the use of 3 main methods, and numerous members, giving maximum  ||
    //  ||  flexibility and efficieny. For more information, -= see the READ ME =-     ||
    //  |+-----------------------------------------------------------------------------+|
    //  ||  The newer versions (3.0 and above) also allow optional extensions, which   ||
    //  ||  enhance UAC with additional members and methods if they're present.        ||
    //  |+-----------------------------------------------------------------------------+|
    //  ||  This controls how often units are checked to see if they are affected by   ||
    public constant real TIMEOUT = 0.03125 // The auras. timerLoop and unitPeriodic    ||
    //  ||  are called at this frequency. .03125 is recommended.                       ||
    //  |+-----------------------------------------------------------------------------+|
    //   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    // =======================================================================================
    
    
            // if debug mode is enabled AND this is true, then error/warning messages will be
            // displayed when something goes wrong or could potentially go wrong.
            debug constant boolean UAC__DEBUG = true
        
        endglobals
        
        // Optional Modules:
        // ------------------
        
            // At top of struct
            private module MainModules 
                implement optional UACEffects
                implement optional UACFilters
            endmodule
            
            // When instance is created
            private module CreateModules
                implement optional UACEffects_Create
            endmodule
            
            // When a unit leaves
            private module LeaveModules
                implement optional UACEffects_Leave
            endmodule
            
            // When a unit enters
            private module EnterModules
                implement optional UACEffects_Enter
            endmodule
            
            // Every TIMEOUT seconds.
            private module PeriodicModules
            
            endmodule
            
            // During the filter stage
            private module FilterModules
                implement optional UACFilters_Filter
            endmodule
        
        // ---------------------
    
        // Interface for optional methods
        private interface UACInterface
            method unitEnter    takes unit whichUnit returns nothing defaults nothing
            method unitLeave    takes unit whichUnit returns nothing defaults nothing
            method unitPeriodic takes unit whichUnit returns nothing defaults nothing
            
            method mainDeath    takes nothing        returns nothing defaults nothing
            method mainRevive   takes nothing        returns nothing defaults nothing
            method timerLoop    takes nothing        returns nothing defaults nothing
            
            // It's a shame that '.exists' can't be used with stub methods.
            method orFilterA    takes unit whichUnit returns boolean defaults false
            method orFilterB    takes unit whichUnit returns boolean defaults false
            method orFilterC    takes unit whichUnit returns boolean defaults false
        endinterface
        
        // Filter methods.
        //! textmacro UAC__Filter takes X
            stub method andFilter$X$ takes unit whichUnit returns boolean
                return true
            endmethod
        //! endtextmacro    
        
        struct UAC extends UACInterface
            // Main unit:
            readonly unit main
            
            // mainUnit can be used instead of main. =)
            method operator mainUnit takes nothing returns unit
                return .main
            endmethod
            
            // 'u' prefix because the names would be taken for safer method operators.
            private real urange     // aura range
            private boolean upause  // is the unit paused?
            private group affected  // holds the affected units
            
            boolean single // should it be destroyed when the mainunit dies?
            readonly integer count = 0 // the amount of units affected by an aura.
            
            // Tickers
            private real uticker = TIMEOUT
            private real ticks = 0.
            
            // struct stack
            private static timer T = CreateTimer() 
            private static integer N = 0
            private static thistype array D
            private integer index
            
            // temporary group filled with all units in range
            private static group fill = CreateGroup()
            
            // code variables used so I can order the methods in the struct as I please.
            // (As long as these are at the top, and onInit is at the bottom)
            private static code funcexpire
            private static code callbacka
            private static code callbackb
            private static code unaffect
            private static boolexpr mainfilt
            
            // Temporary globals
            private static unit tempunit
            private static boolean tempbool
            private static thistype temp
            
            private static hashtable allUACs = InitHashtable() // .typeid, handleId
            
            // Optional Modules:
            implement MainModules
            // -----------------
            
            static method create takes unit mainUnit, real range returns thistype
                local thistype this
                
                // What's the point in creating the struct if the unit could be null?
                if mainUnit == null then
                    debug if UAC__DEBUG then
                    debug   call BJDebugMsg("|cFFFF0000UAC Error:|r Attempt to create aura on null unit.")
                    debug endif
                    return thistype(0)
                endif   
                
                set this = .allocate()
                
                // Setting basic members
                set .main = mainUnit
                set .urange = range
                
                // If the range is <= 0, then we pause the struct. It'd be pointless to be constantly calling functions
                // if no units are going to be affected anyway.
                set .upause = range <= 0
                
                debug if UAC__DEBUG and range <= 0 then
                debug   call BJDebugMsg("|cffffcc00UAC Warning:|r Instance created with 0 range. It has been automatically paused.")
                debug endif
                
                // By default, the struct will be automatically destroyed if mainunit isn't a hero.
                // Otherwise, it'll remain, but be paused.
                set .single = IsUnitType(mainUnit, UNIT_TYPE_HERO) == false
                
                if .affected == null then
                    set .affected = CreateGroup()
                else
                    call GroupClear(.affected)
                endif
                
                // Optional Modules:
                implement CreateModules
                // -----------------
                
                // 'Attaching' the struct to the main unit for use with .get method.
                call SaveInteger(.allUACs, .typeid, GetHandleId(mainUnit), this)
                set .D[.N] = this
                set .index = .N
                set .N = .N + 1
                if .N == 1 then
                    call TimerStart(.T, TIMEOUT, true, .funcexpire)
                endif
                
                return this
            endmethod
            
            // Returns the struct for the aura for a main unit.
            static method get takes unit mainUnit returns thistype
                debug if UAC__DEBUG and mainUnit == null then
                debug   call BJDebugMsg("|cFFFF0000UAC Error:|r Attempt to get a struct attached to a null unit.")
                debug endif
                return LoadInteger(.allUACs, .typeid, GetHandleId(mainUnit))
            endmethod
            
            // If the unit is affected by the aura, this'll return true. 
            method isAffected takes unit whichUnit returns boolean
                debug if UAC__DEBUG and whichUnit == null then
                debug   call BJDebugMsg("|cFFFF0000UAC Error:|r Attempt to check if null unit is affected")
                debug endif
                return IsUnitInGroup(whichUnit, .affected)
            endmethod
            
            // Pause/unpause the struct. If it's being paused, then un-affect all units.
            method operator pause= takes boolean shouldPause returns nothing
                if not .upause and shouldPause then
                    set .temp = this
                    call ForGroup(.affected, .unaffect)
                endif
                
                // Attempting to unpause a struct with 0 range?
                if .urange <= 0 and shouldPause == false then
                
                    debug if UAC__DEBUG then
                    debug    call BJDebugMsg("|cffffcc00UAC Warning:|r Attempt to unpause struct with 0 range - unpause failed.")
                    debug endif
                    
                    set .upause = true
                    return
                else
                    set .upause = shouldPause
                endif
            endmethod
            
            // Is the struct paused?
            method operator pause takes nothing returns boolean
                return .upause
            endmethod
            
            // Set how often the periodic functions should be called.
            method operator ticker= takes real timeout returns nothing
                // Below TIMEOUT?
                if timeout < TIMEOUT then
                    debug if UAC__DEBUG then
                    debug    call BJDebugMsg("|cffffcc00UAC Warning:|r Attempt to set a ticker to below TIMEOUT seconds.")
                    debug endif
                    set .uticker = TIMEOUT
                else
                    // Must be a multiple of TIMEOUT
                    set .uticker = I2R(R2I(timeout / TIMEOUT)) * TIMEOUT
                endif
                set .ticks = 0.
            endmethod
            
            // How often does it expire?
            method operator ticker takes nothing returns real
                return .uticker
            endmethod
            
            // Sets the range of the aura
            method operator range= takes real range returns nothing
                if range <= 0 then // Fool! Setting range to <= 0
                    
                    debug if UAC__DEBUG then
                    debug    call BJDebugMsg("|cffffcc00UAC Warning:|r Attempt to set a structs range to 0. Instance paused.")
                    debug endif
                    
                    // We'll need to pause it.
                    set .pause = true
                endif
                
                // If it was paused previously due to invalid range, and the range is valid,
                // now would be the time to unpause it.
                if .urange <= 0 and .upause then
                    set .pause = false
                endif
                
                set .urange = range
            endmethod
            
            // What's my range again?
            method operator range takes nothing returns real
                return .urange
            endmethod
            
            static method expire takes nothing returns nothing
                local integer i = 0
                local thistype this
                loop
                    exitwhen i == .N
                        set this = .D[i]
                        
                        set .ticks = .ticks + TIMEOUT // Increasing the ticker
                        
                        // Is it time to expire?
                        if .ticks >= .uticker then
                        
                            set .ticks = 0. // Resetting the ticks.
                        
                            if not .upause then // We don't want to fill paused instances.
                                set .temp = this
                                
                                // Fill is first filled with all units in range, matching the condition.
                                // Then the units that are affected, and the units in fill are compared;
                                // if there are units in fill that aren't affected, they become affected.
                                // If there are units that are affected, but aren't in fill, they stop being affected.
                                call GroupEnumUnitsInRange(.fill, GetUnitX(.main), GetUnitY(.main), .urange, .mainfilt)
                                call ForGroup(.fill, .callbacka)
                                call ForGroup(.affected, .callbackb)
                                
                            endif
                            
                            // timerLoop is just a time-saving method which is called every TIMEOUT, regardless of any conditions.
                            if .timerLoop.exists then
                                call .timerLoop()
                            endif
                            
                        endif
                        
                    set i = i + 1
                endloop
            endmethod
            
            static method callbackA takes nothing returns nothing
                set .tempunit = GetEnumUnit()
                
                // Every unit here is a valid target for the aura. If the unit
                // is not affected, it means it recently came within range, or 
                // is now meeting the conditions. We need to affect the unit with
                // the aura.
                
                if not IsUnitInGroup(.tempunit, .temp.affected) then
                    
                    // Optional Modules:
                    implement EnterModules
                    // -----------------
                    
                    set .temp.count = .temp.count + 1
                    
                    if .temp.unitEnter.exists then
                        call .temp.unitEnter(.tempunit)
                    endif
                    
                    call GroupAddUnit(.temp.affected, .tempunit)
                    
                endif
            endmethod
            
            static method callbackB takes nothing returns nothing
                set .tempunit = GetEnumUnit()
                
                // Every unit here is affected by the aura, though some of which
                // may no longer be valid targets. We compare the units here to 
                // the ones that were valid targets. If it's not a valid target, 
                // then the unit becomes no longer affected.
                
                if not IsUnitInGroup(.tempunit, .fill) then
                
                    // Optional Modules:
                    implement LeaveModules
                    // -----------------
                    
                    set .temp.count = .temp.count - 1
                    
                    if .temp.unitLeave.exists then
                        call .temp.unitLeave(.tempunit)
                    endif
                    
                    call GroupRemoveUnit(.temp.affected, .tempunit)
                    
                else
                    
                    // Optional Modules:
                    implement PeriodicModules
                    // -----------------
                
                    // If the unit is a valid target, then we need to call the
                    // periodic function (called for every valid target).
                    // This wasn't called previously as some targets might've been invalid.
                    if .temp.unitPeriodic.exists then
                        call .temp.unitPeriodic(.tempunit)
                    endif
                    
                endif
            endmethod
            
            private static method clearAffected takes nothing returns nothing
                set .tempunit = GetEnumUnit()
                
                // Optional Modules:
                implement LeaveModules
                // -----------------
                
                set .temp.count = .temp.count - 1
                
                if .temp.unitLeave.exists then
                    call .temp.unitLeave(.tempunit)
                endif
                
                call GroupRemoveUnit(.temp.affected, .tempunit)
            endmethod
            
            // Filters:
            //! runtextmacro UAC__Filter("A")
            //! runtextmacro UAC__Filter("B")
            //! runtextmacro UAC__Filter("C")
            
            private static method unitFilter takes nothing returns boolean
                // Checking if units in range can be affected by the aura
                set .tempunit = GetFilterUnit()
                if not .temp.orFilterA.exists and not .temp.orFilterB.exists and not .temp.orFilterC.exists then
                    set .tempbool = (.temp.andFilterA(.tempunit) and .temp.andFilterB(.tempunit) and .temp.andFilterC(.tempunit))
                else
                    set .tempbool = (.temp.andFilterA(.tempunit) and .temp.andFilterB(.tempunit) and .temp.andFilterC(.tempunit)) and (.temp.orFilterA(.tempunit) or .temp.orFilterB(.tempunit) or .temp.orFilterC(.tempunit))
                endif
                
                // Optional Module:
                implement FilterModules
                // ----------------
                
                return .tempbool
            endmethod
            
            // This method is called when a unit dies.
            private static method deathCond takes nothing returns boolean
                set .tempunit = GetTriggerUnit()
                set .temp = .get(.tempunit)
                
                // The dying unit didn't have an aura. =(
                if .temp == 0 then
                    return false
                endif
                
                if .temp.mainDeath.exists then
                    call .temp.mainDeath()
                endif
                
                // Both of these actions clear the affected units.
                if .temp.single then
                    // The main unit died. Destroy the struct if it's meant to only last for one 'life'
                    call .destroy(.temp)
                else
                    // Else, pause it.
                    set .temp.pause = true
                endif
                
                return false
            endmethod
            
            // This method is called when a hero revives
            private static method awakenCond takes nothing returns boolean
                set .tempunit = GetRevivingUnit()
                set .temp = .get(.tempunit)
                
                // The reviving hero didn't have an aura. =(
                if .temp == 0 then
                    return false
                endif
                
                if .temp.mainRevive.exists then
                    call .temp.mainRevive()
                endif
                
                // Unpause it.. If possible.
                set .temp.pause = false
                
                return false
            endmethod
            
            method onDestroy takes nothing returns nothing
                // Clearing the struct 'attachment'
                
                // call RemoveSavedInteger(.allUACs, .typeid, GetHandleId(.main))
                // Apparently, RemoveSavedInteger doesn't work.
                call SaveInteger(.allUACs, .typeid, GetHandleId(.main), 0)
                call ForGroup(.affected, .unaffect) // And unaffecting the units
                
                set .N = .N - 1
                set .D[.index] = .D[.N]
                set .D[.index].index = .index
                if .N == 0 then
                    call PauseTimer(.T)
                endif
            endmethod
            
            private static method onInit takes nothing returns nothing
                // Trigger created so that the struct can be appropriately handled when the mainunit dies.
                local trigger t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
                call TriggerAddCondition(t, Filter(function thistype.deathCond))
                
                // Trigger created so that the struct can be resumed if the mainunit is revived.
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_REVIVE_FINISH)
                call TriggerAddCondition(t, Filter(function thistype.awakenCond))
                
                // Using variables so that the code can be arranged in any order.
                set .funcexpire = function thistype.expire
                set .mainfilt = Filter(function thistype.unitFilter)
                set .callbacka = function thistype.callbackA
                set .callbackb = function thistype.callbackB
                set .unaffect = function thistype.clearAffected
            endmethod
            
        endstruct
    
    endlibrary

    Note that this requires Jasshelper 0.9.G.0 or newer, and Patch 1.23b or newer!

    The Extensions and an Example are in the [POST=912022]next post![/POST]
     

    Attached Files:

    • UAC.w3x
      File size:
      52.8 KB
      Views:
      400
    • Like Like x 4
  2. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    Effects Extension:
    JASS:
    .
       _   _  _   ___                                         
      | | | |/_\ / __|     U N I T   A U R A   C O N T R O L
      | |_| / _ \ (__             B y   R o m e k
       \___/_/ \_\___|              
    
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                -- == E F F E C T S   E X T E N S I O N == --
                               Version 1.1
    _________________________________________________________________
    
        UAC Effects?
        ¯¯¯¯¯¯¯¯¯¯¯¯
            UAC Effects is an optional extension made for UAC. It adds some new members
            which allow the user to easily add effects for every unit affected.
            This is generally used to add a little indicator that the unit is affected, such as the
            general aura target (The little blue glow).
            
        How to Implement:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
            - Copy the UAC Effects trigger into your map
            - Change the constants at the top if you want
            - All the members are automatically added to UAC if the extension is present.
            
        Members:
        ¯¯¯¯¯¯¯¯
            ______________________    
             boolean enableeffect     If enableeffect is true, then a special effect will be added to 
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    every affected unit. Otherwise, the effects will not be added.
                    This can be changed for every instance of every type of aura.
                    
                    set data.enableeffect = true
                    
            ______________________
             string effectpath        This is the path of the model that will be attached to the unit.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    'effectpath' is DEFAULT_PATH by default (see the configurable
                    at the top of the scope). If this is set to "" or null, then the effect is disabled.
                    
                    set data.effectpath = "SomeModel.mdl"
                    
            ______________________
             string effectpoint       This is the point on the affected unit to which the special effect
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    is attached. This is DEFAULT_POINT by default, and I recommend using 
                    the default value whenever possible. If this is set to "" or null, then the effect is 
                    disabled.
                    
                    set data.effecpoint = "origin"
                
            
            
        Changelog:
        ¯¯¯¯¯¯¯¯¯¯
            1.1
            - Fixed a minor bug which made units recieve effects when enableeffect was false.
        
            1.0
            - Initial Release

    JASS:
    scope UACEffects
    globals
    //           _______________________________________________________________
    //          +---------------------------------------------------------------+
    //          ||     _   _  _   ___                                          ||
    //          ||    | | | |/_\ / __|     U N I T   A U R A   C O N T R O L   ||
    //          ||    | |_| / _ \ (__             B y   R o m e k              ||
    //          ||     \___/_/ \_\___|                                         ||
    //   _______||_____________________________________________________________||_______
    //  ||¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯||
    //  ||               -- ==  E F F E C T S   E X T E N S I O N  == --               ||
    //  ||                                Version 1.1                                  ||
    //  ||               ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯               ||
    //  ||  This extension allows the user to easily add an effect to every unit that  ||
    //  ||  is affected by the aura. For more information, see the READ ME.            ||
    //  |+-----------------------------------------------------------------------------+|
    //  ||  The default effect that will be attached to the affected units.            ||__________________________
    private constant string DEFAULT_PATH = "Abilities\\Spells\\Other\\GeneralAuraTarget\\GeneralAuraTarget.mdl" // |
    //  ||                                                                             ||¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  ||  The default attachment point of the effect:                                ||
    private constant string DEFAULT_POINT = "origin"                       //          ||
    //  |+-----------------------------------------------------------------------------+|
    //   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    endglobals
        
        module UACEffects
            string uaceffects_path = DEFAULT_PATH
            string uaceffects_point = DEFAULT_POINT
            boolean uaceffects_enable = false
            group uaceffects_affected
            static hashtable uaceffects_table = InitHashtable()
            static thistype uaceffects_temp
            
            private static method uaceffectsCallback takes nothing returns nothing
                local unit u = GetEnumUnit()
                call GroupRemoveUnit(.uaceffects_temp.uaceffects_affected, u)
                call DestroyEffect(LoadEffectHandle(.uaceffects_table, .uaceffects_temp, GetHandleId(u)))
            endmethod
            
            method operator enableeffect= takes boolean enable returns nothing
                if .uaceffects_enable and not enable then
                    set .uaceffects_temp = this
                    call ForGroup(.uaceffects_affected, function thistype.uaceffectsCallback)
                endif   
                set .uaceffects_enable = enable
            endmethod
            method operator enableeffect takes nothing returns boolean
                return .uaceffects_enable
            endmethod
            
            method operator effectpath= takes string newPath returns nothing
                if newPath == "" or newPath == null then
                    set .enableeffect = false
                    set .uaceffects_path = DEFAULT_PATH
                    return
                endif
                set .uaceffects_path = newPath
                set .enableeffect = .uaceffects_point != ""
            endmethod
            method operator effectpath takes nothing returns string
                return .uaceffects_path
            endmethod
            
            method operator effectpoint= takes string newPoint returns nothing
                if newPoint == "" or newPoint == null then
                    set .enableeffect = false
                    set .uaceffects_point = DEFAULT_POINT
                    return
                endif
                set .uaceffects_point = newPoint
                set .enableeffect = .uaceffects_path != ""
            endmethod
            method operator effectpoint takes nothing returns string
                return .uaceffects_point
            endmethod
        endmodule
        
        module UACEffects_Enter
            if .temp.uaceffects_enable and not IsUnitInGroup(.tempunit, .temp.uaceffects_affected) then
                call GroupAddUnit(.temp.uaceffects_affected, .tempunit)
                call SaveEffectHandle(.uaceffects_table, .temp, GetHandleId(.tempunit), AddSpecialEffectTarget(.temp.uaceffects_path, .tempunit, .temp.uaceffects_point))
            endif
        endmodule
        
        module UACEffects_Leave
            if .temp.uaceffects_enable and IsUnitInGroup(.tempunit, .temp.uaceffects_affected) then
                call GroupRemoveUnit(.temp.uaceffects_affected, .tempunit)
                call DestroyEffect(LoadEffectHandle(.uaceffects_table, .temp, GetHandleId(.tempunit)))
            endif   
        endmodule
        
        module UACEffects_Create
            if .uaceffects_affected == null then
                set .uaceffects_affected = CreateGroup()
            else
                call GroupClear(.uaceffects_affected)
            endif
        endmodule
    
    endscope

    Filters Extension:
    JASS:
    .
       _   _  _   ___                                         
      | | | |/_\ / __|     U N I T   A U R A   C O N T R O L
      | |_| / _ \ (__             B y   R o m e k
       \___/_/ \_\___|              
    
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                -- == F I L T E R S   E X T E N S I O N == --
                               Version 1.2
    _________________________________________________________________
    
        UAC Filters?
        ¯¯¯¯¯¯¯¯¯¯¯¯
            UAC Filters is an optional extension for UAC, which allows the user to easily
            change which units can be affected by the aura, in a user-friendly way without using
            any filter methods.
            
        How to Implement:
        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
            - Copy the UAC Filters trigger into your map
            - All the members are automatically added to UAC if the extension is present.
            
        Members:
        ¯¯¯¯¯¯¯¯
            ______________________    
             boolean allies           If this is true, allies of the main unit can be affected by the aura.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This is true by default.
                    
                    set data.allies = false
                 
            ______________________    
             boolean enemies          If this is true, enemies of the main unit can be affected by the aura.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This is true by default.
                    
                    set data.enemies = false
                 
            ______________________    
             boolean neutral          If this is true, units neutral to the mainunit can be affected by the aura
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This is false by default.
                    
                    set data.neutral = true
                    
            ______________________    
             boolean visible          If this is true, only units that are visible to the mainunit will be affected
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    by the aura. Otherwise, invisible units can be affected too.
                    This is false by default.
                    
                    set data.visible = true
                    
            ______________________    
             boolean self             If this is false, the mainunit cannot be affected by the aura.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This is true by default. This has no effect if allies is false.
                    
                    set data.self = false
                    
            ______________________    
             boolean dead             If this is false, dead units cannot be affected.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    This will probably always be false, unless UAC is being used to
                    give some sort of bonuses depending on dead units within range.
                    If this is true, ONLY dead units will be affected.
                    This is false by default.
                    
                    set data.dead = true
                    
            ______________________    
             unittype requirement     Units MUST be this unittype to be affected by the aura.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    Set this to null if you do not want any requirement.
                    
                    set data.requirement = UNIT_TYPE_GROUND
                    
            ______________________    
             unittype exception       Units that are this unittype will not be affected by the aura.
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯    Set this to null if you do not want any exception.
                    
                    set data.exception = UNIT_TYPE_STRUCTURE
            
            
        Changelog:
        ¯¯¯¯¯¯¯¯¯¯
            1.2
            - Fixed neutral again.
            
            1.1
            - Fixed a bug which stopped allied units being affected when .neutral was false. 
        
            1.0
            - Initial Release

    JASS:
    scope UACFilters
    //           _______________________________________________________________
    //          +---------------------------------------------------------------+
    //          ||     _   _  _   ___                                          ||
    //          ||    | | | |/_\ / __|     U N I T   A U R A   C O N T R O L   ||
    //          ||    | |_| / _ \ (__             B y   R o m e k              ||
    //          ||     \___/_/ \_\___|                                         ||
    //   _______||_____________________________________________________________||_______
    //  ||¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯||
    //  ||                 -- ==  F I L T E R S  E X T E N S I O N  == --              ||
    //  ||                                Version 1.2                                  ||
    //  ||               ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯               ||
    //  ||  This extension allows the user to modify the targets allowed for auras in  ||
    //  ||  a more user-friendly way than using or/andFilters. It introduces a set of  ||
    //  ||  members which can be set to allow different targets.                       ||
    //  |+-----------------------------------------------------------------------------+|
    //   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
        module UACFilters
            boolean allies = true
            boolean enemies = true
            boolean neutral = false
            boolean visible = false
            boolean self = true
            boolean dead = false
            
            unittype requirement = null
            unittype exception = null
            
            static player uacfilters_player
        endmodule
    
    
        module UACFilters_Filter
            if .tempbool == false then
                return false
            endif
            
            set .uacfilters_player = GetOwningPlayer(.temp.main)
            
            set .tempbool = IsUnitAlly(.tempunit, .uacfilters_player) and GetPlayerAlliance(.uacfilters_player, GetOwningPlayer(.tempunit), ALLIANCE_HELP_REQUEST)
            
            if not .temp.allies and .tempbool then
                return false
            elseif not .temp.enemies and IsUnitEnemy(.tempunit, .uacfilters_player) then
                return false
            elseif not .temp.neutral and IsUnitAlly(.tempunit, .uacfilters_player) and not .tempbool then
                return false
            elseif .temp.visible and not IsUnitVisible(.tempunit, .uacfilters_player) then
                return false
            elseif not .temp.self and .tempunit == .temp.main then
                return false
            elseif .temp.dead != (GetWidgetLife(.tempunit) <= 0.405 or IsUnitType(.tempunit, UNIT_TYPE_DEAD) == true) then
                return false
            elseif .temp.exception != null and IsUnitType(.tempunit, .temp.exception) == true then
                return false
            elseif .temp.requirement != null and IsUnitType(.tempunit, .temp.requirement) == false then
                return false
            else
                return true
            endif
            
        endmodule
        
    endscope
     
  3. Grundy

    Grundy Ultra Cool Member

    Ratings:
    +35 / 0 / -0
    Cool. That does look pretty easy to use.

    Can you set the TIMEOUT for each aura? Like what if i have a spell similar to your example that does something every 0.1 seconds but i have another aura that casts Raise Dead on a random corpse within x range every 5 seconds

    -edit-

    Well, I just looked over the code and i see TIMEOUT is a global constant so every aura uses the same TIMEOUT. What if you put a variable in your struct called timeout and it initializes to what the global is set to but the you can change it inside of each struct that extends UAC


    ---------------

    I wrote some code to do projectiles for me... The way I'm doing it, it works but it kind of annoys me. I think I'm going to rewrite it to work more like how your auras work. Thanks for giving me this idea.
     
  4. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    JASS:
                if .FAmount == 5 then
                    debug call BJDebugMsg("UAC Error: Too many Filters added to instance")
                    return
                elseif .FAmount == 0 then
                    set .F1 = wf
                elseif .FAmount == 1 then
                    set .F2 = wf
                elseif .FAmount == 2 then
                    set .F3 = wf
                elseif .FAmount == 3 then
                    set .F4 = wf
                elseif .FAmount == 4 then
                    set .F5 = wf
                endif

    (and everything else to do with the filters)

    Why not use arrays? Would be far simpler, and would allow for a configurable number of filter functions (some people may not want/need 5, and others may want alot :p)

    Also, in your example, why not just do:
    JASS:
    local Data d = Data[GetTriggerUnit ()]
    if d == 0 then
    //create new
    else
    //retrieve from unit
    endif

    :D
     
  5. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    That would decrease the instance limit on the structs.
    And there would be a limit either way. (You need to specifiy the array size).
    5 is too much anyway, as you can have mulitple 'conditions' in a single filter. (Is unit not a structure, flying and ally, for example)

    It doesn't make much difference.
    Although I do admit it's easier to read.

    I'll probably fix it up later.
     
  6. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    1638 (by default) auras using this system, in existence, without any unused? That's quite an unlikely situation (you'd need a huge amount of units, or a number of auras on each unit, both of which are quite unlikely)

    Anyway, you can always reduce the value if needs be (since 5 filters probably won't be needed anyway, which makes the above situation even less likely)
     
  7. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    I don't think there's a point in making it an array. It works fine the way it is.
    And anyway, I feel the system is better with 8191 possible instances than 1638. Even if the latter is near-impossible to reach :p

    Edit:

    Ever heard of "xecollider"?
     
  8. Grundy

    Grundy Ultra Cool Member

    Ratings:
    +35 / 0 / -0
    No, I have not.

    What is that?
     
  9. saw792

    saw792 Is known to say things. That is all.

    Ratings:
    +280 / 0 / -0
  10. Furby

    Furby Current occupation: News poster

    Ratings:
    +140 / 0 / -0
    Well, this is really useful. :thup: It is making auras like Crtitical Strike Aura much easier to make. :)
    +rep :thup:
     
  11. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    Thanks.
    And yes it is.
    Just use the UnitEnterRange method, and give the units the critical strike ability.
    And then the UnitLeaveRange method, to remove the ability. :D
     
  12. Furby

    Furby Current occupation: News poster

    Ratings:
    +140 / 0 / -0
    This should be approved! :thup:
     
  13. Renendaru

    Renendaru (Evol)ution is nothing without love.

    Ratings:
    +309 / 0 / -0
    Nevermind, I re-read. Looks like it triggers when a unit enters range of a unit and such...
     
    • Like Like x 1
  14. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    Yeah. That's exactly what it does.
    Although it doesn't really 'trigger'.
    The appropriate function is simply called.
     
  15. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    Updated!
    • v1.4
      • Recoded the entire system to improve on efficiency and fix some sloppy coding.
      • The main functions now take 2 arguments. (enumerated unit, and main unit)
      • Renamed UnitInRange to UnitPeriodic
      • Renamed UnitEntersRange to UnitEnter
      • Renamed UnitLeavesRange to UnitLeave
      • Filters have been completely revamped. They are now optional functions, and there are 'And' and 'Or' filters. At least one of the 'Or' ones has to be true, and all of the 'And's have to be true. They are all optional.
      • As a result of the filters being remade, the filter library is now removed. (Didn't last long!)

    Have fun! :D

    ~23 Hour bump. I guess that's not too early.
     
  16. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,489 / 0 / -0
    Is there a way to remove an aura from a unit? Any potential leaks when I do? Or when the unit dies?
     
  17. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    JASS:
    call data.Destroy()


    Clears the instance. No leaks, no hassle. (There is an onDestroy method :))
    It doesn't remove it automatically when the unit dies, although the effects stop.

    You could easily make a trigger which destroys the struct when the 'caster' dies.
     
  18. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,489 / 0 / -0
    > You could easily make a trigger which destroys the struct when the 'caster' dies.

    Does the doc say so somewhere? :p
     
  19. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    It does now. :thup:
    Under 'usage'.

    You've also given me a useful idea, which I will add in the next version (Automatically destroying the struct when the main unit dies. Will be disabled by default, but can be enabled with a single function call).
     
  20. ronaldo

    ronaldo New Member

    Ratings:
    +0 / 0 / -0
    I will try to use it on my map, but im not good at jass yet, so i will probably just change effect of your demo spell.
    Looks nice.
    Thanks.
     

Share This Page