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

[vJass] (System) CustomFilter

As already promised, I did also an FilterSystem.

Its features. (Please do NOT ask how or why this is better...)
  • You can do your own filters.
  • They are faster as any other system
  • They are more flexible
  • you can connect them with OR, AND, NOR, NAND
  • You can limit your enums easily
  • easy to use
  • You can also connect Unit and Player filters :p
  • Also, having 4 enumeration boolexpr only with unlimited filters ROCK.
  • You don't need new handles for new filters.
  • You can connect filters endless. (You can have endless filters on a single enumeration in theory)
  • Could make a new standard
  • Gives new functionality
  • AVOID doubled entries! (Enumerations can now only enumerate once per filtering) (Mass Enumeration for a line for example)

Main Library
JASS:
//: §§ ----------------------------------------------------------------------------------------- §§
//: ||  
//: ||      CustomFilter
//: ||  ################
//: ||  by Anachron
//: ||  07.03.2010 20:24
//: ||  v. 0.0.1
//: ||  
//: ||  Advanced filtering engine that provides you with cool, new and easy functions that
//: ||  can become very handy and useable.
//: ||  
//: ||  $ Misc
//: ||  ---------
//: ||  Have fun.
//: ||  
//: §§ ----------------------------------------------------------------------------------------- §§
library CustomFilter initializer init

    private function interface onFilterEvent    takes nothing  returns nothing
    private function interface Cond             takes nothing  returns boolean
    
    private keyword CustomFilter
    private keyword BoolFilter
    globals
        //: You can use this to return true/false everytime on a filter
        BoolFilter    TRUE_FILTER      = 0
        BoolFilter    FALSE_FILTER     = 0
        
        //: A boolean whether to show or hide enumeration info
        //: This helps you whether to see if its working or not.
        private constant boolean    SHOW_ENUM   = true
        
        group   FILTER_GROUP = CreateGroup()
        private hashtable       FilterData    = InitHashtable()
    endglobals
    
    private struct CustomFilter
        public boolean negate = false
        
        private stub method validate takes nothing returns boolean
            return true
        endmethod
        
        public method filterValidate takes nothing returns boolean
            if .negate then
                return not.validate()
            endif
            return .validate()
        endmethod
        
        private stub method onPrepare takes nothing returns nothing
        endmethod
        
        public stub method prepare takes nothing returns nothing
            call .onPrepare()
        endmethod
    endstruct
    
    struct AndFilter extends CustomFilter
        private CustomFilter filterOne = 0
        private CustomFilter filterTwo = 0
        
        public static method create takes CustomFilter f1, CustomFilter f2, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set .filterOne = f1
            set .filterTwo = f2
            set .negate = neg
            
            return this
        endmethod
        
        public method onPrepare takes nothing returns nothing
            call .filterOne.prepare()
            call .filterOne.prepare()
        endmethod
        
        private method validate takes nothing returns boolean
            return .filterOne.filterValidate() and .filterTwo.filterValidate()
        endmethod
    endstruct
    
    struct OrFilter extends CustomFilter
        private CustomFilter filterOne = 0
        private CustomFilter filterTwo = 0
        
        public static method create takes CustomFilter f1, CustomFilter f2, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set .filterOne = f1
            set .filterTwo = f2
            set .negate = neg
            
            return this
        endmethod
        
        private method onPrepare takes nothing returns nothing
            call .filterOne.prepare()
            call .filterTwo.prepare()
        endmethod
    
        private method validate takes nothing returns boolean
            return .filterOne.filterValidate() or .filterTwo.filterValidate()
        endmethod
    endstruct
    
    struct LimitedFilter extends CustomFilter
        private CustomFilter    filter  = 0
        private integer         amount  = 0
        private integer         max     = 0
        
        public static method create takes CustomFilter fi, integer m returns thistype
            local thistype this = thistype.allocate()
            
            set .filter = fi
            set .max    = m
            
            return this
        endmethod
        
        private method validate takes nothing returns boolean
            local boolean valid = .filter.filterValidate()
            
            if .amount < .max and valid then
                set .amount = .amount +1
                return true
            endif
            return false
        endmethod
        
        private method onPrepare takes nothing returns nothing
            call .filter.prepare()
            set .amount = 0
        endmethod
    endstruct
    
    struct BoolFilter extends CustomFilter
        public static method create takes boolean b returns thistype
            local thistype this = thistype.allocate()
            set .negate = b
            return this
        endmethod
    endstruct
    
    //! textmacro CustomFilter_createFilter takes CONST, NAME, TYPE, FTYPE
    globals
        private CustomFilter    $NAME$Filter        = TRUE_FILTER
        private onFilterEvent   on$NAME$Event       = 0
                $TYPE$          FILTER_$CONST$      = null
    endglobals
    
    private function filter$NAME$ takes nothing returns boolean
        local boolean hasEnumed = false
        
        set FILTER_$CONST$ = GetFilter$NAME$()
        
        if not HaveSavedInteger(FilterData, $FTYPE$, GetHandleId(FILTER_$CONST$)) then
            call SaveInteger(FilterData, $FTYPE$, GetHandleId(FILTER_$CONST$), -1)
        else
            return false
        endif
        
        if FILTER_$CONST$ == null then
            debug call BJDebugMsg("|cffffcc00" + SCOPE_PREFIX + "$NAME$" + "_Error|r: Filter has no valid " + "$TYPE$" + " to process with.")
            return false
        endif
                
        set hasEnumed = $NAME$Filter.filterValidate()
        
        if hasEnumed then
            debug if SHOW_ENUM then
                debug call BJDebugMsg("|cffffcc00" + SCOPE_PREFIX + "$CONST$" + "_Notice|r: [Enum] [|cffffcc00" + "$TYPE$" + "|r] " + Get$NAME$Name(FILTER_$CONST$) + "(#" + I2S(GetHandleId(FILTER_$CONST$)) + ")")
            debug endif
            
            call on$NAME$Event.execute()
        endif
        
        return hasEnumed
    endfunction
    
    function set$NAME$Filter takes CustomFilter theFilter returns nothing
        set $NAME$Filter = theFilter
        set on$NAME$Event = 0
        call FlushChildHashtable(FilterData, $FTYPE$)
    endfunction
    
    function get$NAME$Filter takes nothing returns CustomFilter
        return $NAME$Filter
    endfunction
    
    function setOn$NAME$Enum takes onFilterEvent theFunc returns nothing
        set on$NAME$Event = theFunc
    endfunction
    
    function getOn$NAME$Enum takes nothing returns onFilterEvent
        return on$NAME$Event
    endfunction
    
    globals
        boolexpr $CONST$_FILTER = null
    endglobals
    
    struct $NAME$CondFilter extends CustomFilter
        private CustomFilter    filter  = 0
        private Cond            cond    = 0
        
        public static method create takes CustomFilter fi, Cond c, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set .filter = fi
            set .cond = c
            set .negate = neg
            
            return this
        endmethod
        
        private method onPrepare takes nothing returns nothing
            call .filter.prepare()
        endmethod
    
        private method validate takes nothing returns boolean
            if not .cond.evaluate() then
                return false
            endif
            return .filter.filterValidate()
        endmethod
    endstruct
    //! endtextmacro
    
    //! runtextmacro CustomFilter_createFilter("DESTRUCTABLE",  "Destructable", "destructable", "0")
    //! runtextmacro CustomFilter_createFilter("ITEM",          "Item",         "item",         "1")
    //! runtextmacro CustomFilter_createFilter("UNIT",          "Unit",         "unit",         "2")
    //! runtextmacro CustomFilter_createFilter("PLAYER",        "Player",       "player",       "3")
    
    //: == § --                     -- §
    //: Here are my default filters.
    //: == § --                     -- §
    struct UnitTypeFilter extends CustomFilter
        private unittype uType = null
        
        public static method create takes unittype t, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set .uType = t
            set .negate = neg
            
            return this
        endmethod
    
        private method validate takes nothing returns boolean
            return IsUnitType(FILTER_UNIT, .uType) 
        endmethod
    endstruct
    
    struct UnitBuffFilter extends CustomFilter
        private integer bType = 0
        
        public static method create takes integer b, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set .bType = b
            set .negate = neg
            
            return this
        endmethod
    
        private method validate takes nothing returns boolean
            return GetUnitAbilityLevel(FILTER_UNIT, .bType) > 0
        endmethod
    endstruct
    
    struct RangeFilter extends CustomFilter
        private CustomFilter filter = 0
        private real distance = 0
        private Loc pos = 0
    
        public static method create takes CustomFilter fi, real x, real y, real d, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set .pos = Loc.create(x, y, 0.)
            set .filter = fi
            set .distance = d
            set .negate = neg
            
            return this
        endmethod
    
        private method validate takes nothing returns boolean
            local Loc       cur = Loc.create(GetUnitX(FILTER_UNIT), GetUnitY(FILTER_UNIT), 0.)
            local boolean   did = .pos.distanceTo(cur) <= .distance
            
            call cur.destroy()
            return did and .filter.filterValidate()
        endmethod
    endstruct
    
    struct AllyFilter extends CustomFilter
        private player allyWith = null
        
        public static method create takes player p, boolean neg returns thistype
            local thistype this = thistype.allocate()
            
            set allyWith = p
            set .negate = neg
            
            return this
        endmethod
        
        private method validate takes nothing returns boolean
            return IsPlayerAlly(GetOwningPlayer(FILTER_UNIT), .allyWith)
        endmethod
    endstruct
    
    //: == § --                     -- §
    //: Place your filters in this section
    //: == § --                     -- §
    
    private function init takes nothing returns nothing
        set TRUE_FILTER = BoolFilter.create(false)
        set FALSE_FILTER = BoolFilter.create(true)
        
        set DESTRUCTABLE_FILTER = Condition(function filterDestructable)
        set ITEM_FILTER = Condition(function filterItem)
        set UNIT_FILTER = Condition(function filterUnit)
        set PLAYER_FILTER = Condition(function filterPlayer)
    endfunction
endlibrary

Location Helper
JASS:
library Loc

    //! textmacro Loc_addOperator takes name, type, object
    method operator $name$= takes $type$ val returns nothing
        set .$object$ = val
    endmethod
    
    method operator $name$ takes nothing returns $type$
        return .$object$
    endmethod
    //! endtextmacro
    
    function makeRealAngle takes real a returns real
        local real radA = 360. * bj_DEGTORAD
        loop
            exitwhen a < radA and a >= 0.
            
            if a < 0 then
                set a = a + radA
            elseif a > radA then
                set a = a - radA
            endif
        endloop
        
        return a
    endfunction
    
    struct Loc
        private real x = 0.
        private real y = 0.
        private real z = 0.
        
        //! runtextmacro Loc_addOperator("X", "real", "x")
        //! runtextmacro Loc_addOperator("Y", "real", "y")
        //! runtextmacro Loc_addOperator("Z", "real", "z")
        
        public static method create takes real x, real y, real z returns thistype
            local thistype this = thistype.allocate()
            
            set .x = x
            set .y = y
            set .z = z
            
            return this
        endmethod
        
        public method angleTo takes thistype obj returns real
            return makeRealAngle(Atan2(obj.y - .y, obj.x - .x))
        endmethod
        
        public method distanceTo takes thistype obj returns real
            local real dx = obj.x - .x
            local real dy = obj.y - .y
            return SquareRoot(dx * dx + dy * dy)
        endmethod
        
        public method move takes real d, real f returns nothing
            set .x = .x + d * Cos(f)
            set .y = .y + d * Sin(f)
        endmethod
    endstruct
endlibrary

Set of predefined filters
JASS:
library CFLib initializer init requires CustomFilter

    globals
        UnitTypeFilter FILTER_UNIT_ANCIENT        = 0
        UnitTypeFilter FILTER_UNIT_ATTACK_AIR     = 0
        UnitTypeFilter FILTER_UNIT_ATTACK_GROUND  = 0
        UnitTypeFilter FILTER_UNIT_DEAD           = 0
        UnitTypeFilter FILTER_UNIT_ETHEREAL       = 0
        UnitTypeFilter FILTER_UNIT_FLYING         = 0
        UnitTypeFilter FILTER_UNIT_GIANT          = 0
        UnitTypeFilter FILTER_UNIT_GROUND         = 0
        UnitTypeFilter FILTER_UNIT_HERO           = 0
        UnitTypeFilter FILTER_UNIT_MAGIC_IMMUNE   = 0
        UnitTypeFilter FILTER_UNIT_MECHANICAL     = 0
        UnitTypeFilter FILTER_UNIT_MELEE          = 0
        UnitTypeFilter FILTER_UNIT_PEON           = 0
        UnitTypeFilter FILTER_UNIT_PLAGUED        = 0
        UnitTypeFilter FILTER_UNIT_POISONED       = 0
        UnitTypeFilter FILTER_UNIT_POLYMORPHED    = 0
        UnitTypeFilter FILTER_UNIT_RANGED         = 0
        UnitTypeFilter FILTER_UNIT_RESISTANT      = 0
        UnitTypeFilter FILTER_UNIT_SAPPER         = 0
        UnitTypeFilter FILTER_UNIT_SLEEPING       = 0
        UnitTypeFilter FILTER_UNIT_SNARED         = 0
        UnitTypeFilter FILTER_UNIT_STRUCTURE      = 0
        UnitTypeFilter FILTER_UNIT_STUNNED        = 0
        UnitTypeFilter FILTER_UNIT_SUMMONED       = 0
        UnitTypeFilter FILTER_UNIT_TAUREN         = 0
        UnitTypeFilter FILTER_UNIT_TOWNHALL       = 0
        UnitTypeFilter FILTER_UNIT_UNDEAD         = 0
        
        UnitTypeFilter FILTER_UNIT_ALIVE              = 0
        UnitTypeFilter FILTER_UNIT_NOT_ANCIENT        = 0
        UnitTypeFilter FILTER_UNIT_NOT_ATTACK_AIR     = 0
        UnitTypeFilter FILTER_UNIT_NOT_ATTACK_GROUND  = 0
        UnitTypeFilter FILTER_UNIT_AWAKE              = 0
        UnitTypeFilter FILTER_UNIT_NOT_DEAD           = 0
        UnitTypeFilter FILTER_UNIT_NOT_ETHEREAL       = 0
        UnitTypeFilter FILTER_UNIT_NOT_FLYING         = 0
        UnitTypeFilter FILTER_UNIT_NOT_GIANT          = 0
        UnitTypeFilter FILTER_UNIT_NOT_GROUND         = 0
        UnitTypeFilter FILTER_UNIT_NOT_HERO           = 0
        UnitTypeFilter FILTER_UNIT_NOT_MAGIC_IMMUNE   = 0
        UnitTypeFilter FILTER_UNIT_NOT_MECHANICAL     = 0
        UnitTypeFilter FILTER_UNIT_NOT_MELEE          = 0
        UnitTypeFilter FILTER_UNIT_NOT_PEON           = 0
        UnitTypeFilter FILTER_UNIT_NOT_PLAGUED        = 0
        UnitTypeFilter FILTER_UNIT_NOT_POISONED       = 0
        UnitTypeFilter FILTER_UNIT_NOT_POLYMORPHED    = 0
        UnitTypeFilter FILTER_UNIT_NOT_RANGED         = 0
        UnitTypeFilter FILTER_UNIT_NOT_RESISTANT      = 0
        UnitTypeFilter FILTER_UNIT_NOT_SAPPER         = 0
        UnitTypeFilter FILTER_UNIT_NOT_SNARED         = 0
        UnitTypeFilter FILTER_UNIT_NOT_STRUCTURE      = 0
        UnitTypeFilter FILTER_UNIT_NOT_STUNNED        = 0
        UnitTypeFilter FILTER_UNIT_NOT_SUMMONED       = 0
        UnitTypeFilter FILTER_UNIT_NOT_TAUREN         = 0
        UnitTypeFilter FILTER_UNIT_NOT_TOWNHALL       = 0
        UnitTypeFilter FILTER_UNIT_NOT_UNDEAD         = 0
        
        UnitBuffFilter FILTER_UNIT_HAS_BUFF_ENSNARED  = 0
    endglobals
    
    private function init takes nothing returns nothing
        //: ==---- 
        //: Create the UnitTypeFilters here.
        //: UnitTypeFilter.create(UnitType, Negate?)
        //: ==----
        set FILTER_UNIT_ANCIENT          = UnitTypeFilter.create(UNIT_TYPE_ANCIENT, false)
        set FILTER_UNIT_ATTACK_AIR       = UnitTypeFilter.create(UNIT_TYPE_ATTACKS_FLYING, false)
        set FILTER_UNIT_ATTACK_GROUND    = UnitTypeFilter.create(UNIT_TYPE_ATTACKS_GROUND, false)
        set FILTER_UNIT_DEAD             = UnitTypeFilter.create(UNIT_TYPE_DEAD, false)
        set FILTER_UNIT_ETHEREAL         = UnitTypeFilter.create(UNIT_TYPE_ETHEREAL, false)
        set FILTER_UNIT_FLYING           = UnitTypeFilter.create(UNIT_TYPE_FLYING, false)
        set FILTER_UNIT_GIANT            = UnitTypeFilter.create(UNIT_TYPE_GIANT, false)
        set FILTER_UNIT_GROUND           = UnitTypeFilter.create(UNIT_TYPE_GROUND, false)
        set FILTER_UNIT_HERO             = UnitTypeFilter.create(UNIT_TYPE_HERO, false)
        set FILTER_UNIT_MAGIC_IMMUNE     = UnitTypeFilter.create(UNIT_TYPE_MAGIC_IMMUNE, false)
        set FILTER_UNIT_MECHANICAL       = UnitTypeFilter.create(UNIT_TYPE_MECHANICAL, false)
        set FILTER_UNIT_MELEE            = UnitTypeFilter.create(UNIT_TYPE_MELEE_ATTACKER, false)
        set FILTER_UNIT_PEON             = UnitTypeFilter.create(UNIT_TYPE_PEON, false)
        set FILTER_UNIT_PLAGUED          = UnitTypeFilter.create(UNIT_TYPE_PLAGUED, false)
        set FILTER_UNIT_POISONED         = UnitTypeFilter.create(UNIT_TYPE_POISONED, false)
        set FILTER_UNIT_POLYMORPHED      = UnitTypeFilter.create(UNIT_TYPE_POLYMORPHED, false)
        set FILTER_UNIT_RANGED           = UnitTypeFilter.create(UNIT_TYPE_RANGED_ATTACKER, false)
        set FILTER_UNIT_RESISTANT        = UnitTypeFilter.create(UNIT_TYPE_RESISTANT, false)
        set FILTER_UNIT_SAPPER           = UnitTypeFilter.create(UNIT_TYPE_SAPPER, false)
        set FILTER_UNIT_SLEEPING         = UnitTypeFilter.create(UNIT_TYPE_SLEEPING, false)
        set FILTER_UNIT_SNARED           = UnitTypeFilter.create(UNIT_TYPE_SNARED, false)
        set FILTER_UNIT_STRUCTURE        = UnitTypeFilter.create(UNIT_TYPE_STRUCTURE, false)
        set FILTER_UNIT_STUNNED          = UnitTypeFilter.create(UNIT_TYPE_STUNNED, false)
        set FILTER_UNIT_SUMMONED         = UnitTypeFilter.create(UNIT_TYPE_SUMMONED, false)
        set FILTER_UNIT_TAUREN           = UnitTypeFilter.create(UNIT_TYPE_TAUREN, false)
        set FILTER_UNIT_TOWNHALL         = UnitTypeFilter.create(UNIT_TYPE_TOWNHALL, false)
        set FILTER_UNIT_UNDEAD           = UnitTypeFilter.create(UNIT_TYPE_UNDEAD, false)
        
        //: ==---- 
        //: Create the negated UnitTypeFilters here.
        //: UnitTypeFilter.create(UnitType, Negate?)
        //: ==----
        set FILTER_UNIT_ALIVE                = UnitTypeFilter.create(UNIT_TYPE_DEAD, true)
        set FILTER_UNIT_NOT_ANCIENT          = UnitTypeFilter.create(UNIT_TYPE_ANCIENT, true)
        set FILTER_UNIT_NOT_ATTACK_AIR       = UnitTypeFilter.create(UNIT_TYPE_ATTACKS_FLYING, true)
        set FILTER_UNIT_NOT_ATTACK_GROUND    = UnitTypeFilter.create(UNIT_TYPE_ATTACKS_GROUND, true)
        set FILTER_UNIT_AWAKE                = UnitTypeFilter.create(UNIT_TYPE_SLEEPING, true)
        set FILTER_UNIT_NOT_ETHEREAL         = UnitTypeFilter.create(UNIT_TYPE_ETHEREAL, true)
        set FILTER_UNIT_NOT_FLYING           = UnitTypeFilter.create(UNIT_TYPE_FLYING, true)
        set FILTER_UNIT_NOT_GIANT            = UnitTypeFilter.create(UNIT_TYPE_GIANT, true)
        set FILTER_UNIT_NOT_GROUND           = UnitTypeFilter.create(UNIT_TYPE_GROUND, true)
        set FILTER_UNIT_NOT_HERO             = UnitTypeFilter.create(UNIT_TYPE_HERO, true)
        set FILTER_UNIT_NOT_MAGIC_IMMUNE     = UnitTypeFilter.create(UNIT_TYPE_MAGIC_IMMUNE, true)
        set FILTER_UNIT_NOT_MECHANICAL       = UnitTypeFilter.create(UNIT_TYPE_MECHANICAL, true)
        set FILTER_UNIT_NOT_MELEE            = UnitTypeFilter.create(UNIT_TYPE_MELEE_ATTACKER, true)
        set FILTER_UNIT_NOT_PEON             = UnitTypeFilter.create(UNIT_TYPE_PEON, true)
        set FILTER_UNIT_NOT_PLAGUED          = UnitTypeFilter.create(UNIT_TYPE_PLAGUED, true)
        set FILTER_UNIT_NOT_POISONED         = UnitTypeFilter.create(UNIT_TYPE_POISONED, true)
        set FILTER_UNIT_NOT_POLYMORPHED      = UnitTypeFilter.create(UNIT_TYPE_POLYMORPHED, true)
        set FILTER_UNIT_NOT_RANGED           = UnitTypeFilter.create(UNIT_TYPE_RANGED_ATTACKER, true)
        set FILTER_UNIT_NOT_RESISTANT        = UnitTypeFilter.create(UNIT_TYPE_RESISTANT, true)
        set FILTER_UNIT_NOT_SAPPER           = UnitTypeFilter.create(UNIT_TYPE_SAPPER, true)
        set FILTER_UNIT_NOT_SNARED           = UnitTypeFilter.create(UNIT_TYPE_SNARED, true)
        set FILTER_UNIT_NOT_STRUCTURE        = UnitTypeFilter.create(UNIT_TYPE_STRUCTURE, true)
        set FILTER_UNIT_NOT_STUNNED          = UnitTypeFilter.create(UNIT_TYPE_STUNNED, true)
        set FILTER_UNIT_NOT_SUMMONED         = UnitTypeFilter.create(UNIT_TYPE_SUMMONED, true)
        set FILTER_UNIT_NOT_TAUREN           = UnitTypeFilter.create(UNIT_TYPE_TAUREN, true)
        set FILTER_UNIT_NOT_TOWNHALL         = UnitTypeFilter.create(UNIT_TYPE_TOWNHALL, true)
        set FILTER_UNIT_NOT_UNDEAD           = UnitTypeFilter.create(UNIT_TYPE_UNDEAD, true)
        
        //: ==---- 
        //: Create the UnitBuffFilters here.
        //: UnitBuffFilter.create(BuffID, Negate?)
        //: ==----
        set FILTER_UNIT_HAS_BUFF_ENSNARED   = UnitBuffFilter.create('esnr', false)
    endfunction

endlibrary

My new awesome enumeration methods!
JASS:
library CFEnum requires CustomFilter, Loc

    globals
        //: The update interval of line enumeration.
        //: You shouldn't use a lower value then 32.
        private constant    real LINE_UPDATE    = 64.
    endglobals

    function EnumUnitsOfPlayer takes group theGroup, player thePlayer returns nothing
        call GroupEnumUnitsOfPlayer(theGroup, thePlayer, UNIT_FILTER)
    endfunction
    
    function EnumUnitsInRange takes group theGroup, real x, real y, real r returns nothing
        call GroupEnumUnitsInRange(theGroup, x, y, r, UNIT_FILTER)
    endfunction
    
    function EnumUnitsInRect takes group theGroup, rect theRect returns nothing
        call GroupEnumUnitsInRect(theGroup, theRect, UNIT_FILTER)
    endfunction

    function EnumUnitsOfPlayersInForce takes group theGroup, force theForce returns nothing
        local integer i = 0
        
        loop
            exitwhen i >= bj_MAX_PLAYERS
            
            if IsPlayerInForce(Player(i), theForce) then
                call EnumUnitsOfPlayer(theGroup, Player(i))
            endif
            set i = i +1
        endloop
    endfunction
    
    //: This enumerates all units that are in range r of the line between Loc(x1, y1) - Loc(x2, y2)
    //:      .----------------.
    //:   n /            p     \
    //:     \     p            /
    //:      .----------------.
    //:                n
    //:     n = not picked
    //:     p = picked
    //:                                     enumGroup      PosX     PosY     PosX2    PosY2    radius
    function EnumUnitsInRangeOfLine takes group theGroup, real x1, real y1, real x2, real y2, real r returns nothing
        local Loc cur = Loc.create(x1, y1, 0.)
        local Loc end = Loc.create(x2, y2, 0.)
        local real face = cur.angleTo(end)
        local integer i = R2I(cur.distanceTo(end) / LINE_UPDATE)
        
        loop
            exitwhen i <= 0
            
            call EnumUnitsInRange(theGroup, cur.X, cur.Y, r)
            call cur.move(LINE_UPDATE, face)
            set i = i -1
        endloop
        
        call cur.destroy()
        call end.destroy()
    endfunction
    
    //: This enumerates all units that are in the circle of Loc(x, y) withing a radius of r, 
    //: but only if their distance to the center is more then r-w. 
    //:  n   /        p     \
    //:     .     .-*"*-.    .       n
    //:    / p   *  n    *    \
    //:    \     *    n  *    /  n
    //:     *     *-.,.-*    *                              
    //:      \              /
    //:       *.__________.*
    //:     n = not picked
    //:     p = picked
    //:                                     enumGroup     PosX    PosY    Radius   Width(Border)
    function EnumUnitsInCircleBorder takes group theGroup, real x, real y, real r, real w returns nothing
        local RangeFilter rf = RangeFilter.create(getUnitFilter(), x, y, r-w, true)
        
        call setUnitFilter(rf)
        call EnumUnitsInRange(theGroup, x, y, r)
        call rf.destroy()
    endfunction

endlibrary

Examples to bring it closer to you!
JASS:
library CustomFilterExamples initializer all requires CustomFilter, CFLib, CFEnum

    //: ------------------------------------------------------------------------
    //: Lets do some basic stuff at first.
    private function e1 takes nothing returns nothing
        //: Create a new group
        local group g = CreateGroup()
        //: and lets use the alive filter
        call setUnitFilter(FILTER_UNIT_ALIVE)
        //: and now lets enumerate units into the group.
        call GroupEnumUnitsInRange(g, 0., 0., 512., UNIT_FILTER)
        //: Lets do a little check if we picked any unit
        call BJDebugMsg("We picked " + I2S(CountUnitsInGroup(g)) + " units!")
    endfunction
    
    //: ------------------------------------------------------------------------
    //: Lets do something more advanced. We pick all units that are non-heroes
    //: and print them.
    private function e2_Print takes nothing returns nothing
        //: Notice how we can access to the global variable
        //: FILTER_UNIT at any place!
        call BJDebugMsg(GetUnitName(FILTER_UNIT))
    endfunction

    private function e2 takes nothing returns nothing
        local group g = CreateGroup()
        call setUnitFilter(FILTER_UNIT_NOT_HERO)
        //: We actually need to link the enum with function
        //: Do this AFTER applying the filter!
        call setOnUnitEnum(e2_Print)
        call GroupEnumUnitsInRange(g, 0., 0., 512., UNIT_FILTER)
    endfunction
    
    //: ------------------------------------------------------------------------
    //: Lets do some advanced filtering this time. 
    //: Who wants all units to be filtered, everytime?
    //: To face it, we want only 5 units that are not undead.
    private function e3 takes nothing returns nothing
        local group g = CreateGroup()
        //: Notice how we can connect filters inside each other!
        local LimitedFilter lf = LimitedFilter.create(FILTER_UNIT_NOT_UNDEAD, 5)
        
        call setUnitFilter(lf)
        call GroupEnumUnitsInRange(g, 0., 0., 512., UNIT_FILTER)
        call lf.destroy()
        
        //: Lets do a little check if we picked any unit
        call BJDebugMsg("We picked " + I2S(CountUnitsInGroup(g)) + " units!")
    endfunction
    
    //: ------------------------------------------------------------------------
    //: We still haven't concatigated conditions yet. 
    //: This time, we want to have an unit that is either dead or alive and 
    //: not an hero
    private function e4 takes nothing returns nothing
        local group g = CreateGroup()
        //: Notice how we can connect this two filters 
        //: with an AND connector.
        local AndFilter ad = AndFilter.create(FILTER_UNIT_ALIVE, FILTER_UNIT_HERO, false)
        //: Notice how we can now connect 2 filters 
        //: of different types with this or filter
        local OrFilter of = OrFilter.create(FILTER_UNIT_DEAD, ad, false)
        
        call setUnitFilter(of)
        call GroupEnumUnitsInRange(g, 0., 0., 512., UNIT_FILTER)
        call of.destroy()
        call ad.destroy()
        
        //: Lets do a little check if we picked any unit
        call BJDebugMsg("We picked " + I2S(CountUnitsInGroup(g)) + " units!")
    endfunction
    
    //: ------------------------------------------------------------------------
    //: Now we want to do something more advanced.
    //: This time we want to have a filter for all units of a spell.
    //: The filter is for disentigrate from Diablo III, so we use the CFEnum
    //: library to get new, great enumeration functions.
    //: For this spell we want to have alive units only, that are enemies.
    globals
        private constant real DISINTEGRATE_RANGE = 128.
    endglobals
    
    private struct e5struct
        private unit caster = null
        private real x1 = -192
        private real y1 = 0.
        private real x2 = 192
        private real y2 = 0.
        
        private static method dmg takes nothing returns nothing
            local real lastHp = GetUnitState(FILTER_UNIT, UNIT_STATE_LIFE)
            local real newHp = lastHp - 25.
            
            call SetUnitState(FILTER_UNIT, UNIT_STATE_LIFE, newHp)
        endmethod
        
        //: This is the method that is run every few ms.
        public method e5 takes nothing returns nothing
            local group g = CreateGroup()
            //: Create a new allyfilter
            local AllyFilter af = AllyFilter.create(Player(0), false)
            //: Create a new andfilter and connect it with ally and alivefilter
            local AndFilter andf = AndFilter.create(af, FILTER_UNIT_ALIVE, false)
            
            call setUnitFilter(andf)
            //: Notice how we again connect enumeration with method calls.
            call setOnUnitEnum(thistype.dmg)
            //: Function of the CFLib
            call EnumUnitsInRangeOfLine(g, .x1, .y1, .x2, .y2, DISINTEGRATE_RANGE)
            call andf.destroy()
            call af.destroy()
        endmethod
    endstruct
    
    //: ------------------------------------------------------------------------
    //: Lets use our own condition functions.
    private function e6_Print takes nothing returns nothing
        //: Notice how we can access to the global variable
        //: FILTER_UNIT at any place!
        call BJDebugMsg(GetUnitName(FILTER_UNIT))
    endfunction
    
    private function e6_Cond takes nothing returns boolean
        return GetUnitState(FILTER_UNIT, UNIT_STATE_LIFE) >= 100.
    endfunction
    
    private function e6 takes nothing returns nothing
        local group g = CreateGroup()
        //: Lets filter all units (TRUE_FILTER) that match our condition
        local UnitCondFilter cf = UnitCondFilter.create(TRUE_FILTER, e6_Cond, false)
        
        call setUnitFilter(cf)
        //: Notice how we again connect enumeration with method calls.
        call setOnUnitEnum(e6_Print)
        call GroupEnumUnitsInRange(g, 0., 0., 512., UNIT_FILTER)
        call cf.destroy()
        //: Thats it. Was is that hard?
    endfunction
    
    //: ------------------------------------------------------------------------
    //: Now you know about a lot of functionality of this system.
    //: Thanks for downloading and using.
    //: ------------------------------------------------------------------------
    
    private function all takes nothing returns nothing
        local e5struct e5i = e5struct.create()
        //call e1()
        //call e2()
        //call e3()
        //call e4()
        call e5i.e5()
        //call e6()
    endfunction
endlibrary

Hint:
For testing I have made a little fix in example 5, the e5 method
JASS:
local AllyFilter af = AllyFilter.create(Player(0), false)
has to be
JASS:
local AllyFilter af = AllyFilter.create(Player(0), true)
to enumerate enemies only. I made it false to check whether it works or not.

Keywords:
Custom, Filter, System, Awesome, Anachron, Group, Unit, Linear, Disintegrate
Contents

CustomFilter 0.0.2 (Map)

Reviews
12:24, 18th Mar 2010 The_Reborn_Devil: The code looks good, but I noticed one thing >:D In the library "Loc" you have a function called "makeRealAngle" which looks like this: function makeRealAngle takes real a returns real local...

Moderator

M

Moderator

12:24, 18th Mar 2010
The_Reborn_Devil:

The code looks good, but I noticed one thing >:D

In the library "Loc" you have a function called "makeRealAngle"
which looks like this:
JASS:
function makeRealAngle takes real a returns real
        local real radA = 360. * bj_DEGTORAD
        loop
            exitwhen a < radA and a >= 0.
            
            if a < 0 then
                set a = a + radA
            elseif a > radA then
                set a = a - radA
            endif
        endloop
        
        return a
    endfunction
First, this local real radA = 360. * bj_DEGTORAD should be a constant with the value 2*PI, 6.283185307179586476925286766559 (you don't need all the decimals).
Second, why need a loop? I don't think anyone will ever give it values above 4*PI.
You could just make the function look like this:
JASS:
globals
    constant real TWOPI = 6.283185307
endglobals

function makeRealAngle takes real a returns real
        if a < 0 then
            set a = a + TWOPI
        elseif a > radA then
            set a = a - TWOPI
        endif
        return a
    endfunction

Other than that it looks really good.


Status: Approved
Rating: Highly Recommended
 
Top