2 GroupUtils in my map.. need to get rid of one of them...help

jig7c

Stop reading me...-statement
Reaction score
123
I use EGUI, and in that progam, there is a knockback trigger, which has a library GroupUtil and i have a knockback spell which i'm assuming uses the above trigger...

now, I tried to import another spell, which also uses GroupUtil and it came with it's own version of GroupUtil.. now I don't know how to make Energy Field work.. (the imported spell)

this code came with EGUI, the knockback trigger... "library GroupUtils" is located towards the end of this script
Code:
//******************************************************************************
//*                                                                            *
//*                             K N O C K B A C K                              *
//*                                Actual Code                                 *
//*                                   v1.06                                    *
//*                                                                            *
//*                              By: Rising_Dusk                               *
//*                                                                            *
//******************************************************************************

library Knockback initializer Init needs TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder
globals
    //*********************************************************
    //* These are the configuration constants for the system
    //*
    //* EFFECT_ATTACH_POINT:  Where on the unit the effect attaches
    //* EFFECT_PATH_WATER:    What special effect to attach over water
    //* EFFECT_PATH_GROUND:   What special effect to attach over ground
    //* DEST_RADIUS:          Radius around which destructs die
    //* DEST_RADIUS_SQUARED:  Radius squared around which destructs die
    //* ADJACENT_RADIUS:      Radius for knocking back adjacent units
    //* ADJACENT_FACTOR:      Factor for collision speed transfers
    //* TIMER_INTERVAL:       The interval for the timer that gets run
    //* ISSUE_LAST_ORDER:     A boolean to issue last orders or not
    //*
    private constant string         EFFECT_ATTACH_POINT = "origin"
    private constant string         EFFECT_PATH_WATER   = "MDX\\KnockbackWater.mdx"
    private constant string         EFFECT_PATH_GROUND  = "MDX\\KnockbackDust.mdx"
    private constant real           DEST_RADIUS         = 180.
    private constant real           DEST_RADIUS_SQUARED = DEST_RADIUS*DEST_RADIUS
    private constant real           ADJACENT_RADIUS     = 180.
    private constant real           ADJACENT_FACTOR     = 0.75
    private constant real           TIMER_INTERVAL      = 0.05
    private constant boolean        ISSUE_LAST_ORDER    = true
    
    //*********************************************************
    //* These are static constants used by the system and shouldn't be changed
    //*
    //* Timer:                The timer that runs all of the effects for the spell
    //* Counter:              The counter for how many KB instances exist
    //* HitIndex:             Indexes for a given unit's knockback
    //* Knockers:             The array of all struct instances that exist
    //* Entries:              Counters for specific unit instances in system
    //* ToClear:              How many instances to remove on next run
    //* DesBoolexpr:          The check used for finding destructables
    //* AdjBoolexpr:          The check for picking adjacent units to knockback
    //* DestRect:             The rect used to check for destructables
    //*
    private          timer          Timer               = CreateTimer()
    private          integer        Counter             = 0
    private          integer  array HitIndex
    private          integer  array Knockers
    private          integer  array Entries
    private          integer  array ToClear
    private          boolexpr       DesBoolexpr         = null
    private          boolexpr       AdjBoolexpr         = null
    private          rect           DestRect            = Rect(0,0,1,1)
    
    //* Temporary variables used by the system
    private          real           TempX               = 0.
    private          real           TempY               = 0.
    private          unit           TempUnit1           = null
    private          unit           TempUnit2           = null
endglobals

//* Functions for the destructable destruction
private function KillDests_Check takes nothing returns boolean
    local real x = GetDestructableX(GetFilterDestructable())
    local real y = GetDestructableY(GetFilterDestructable())
    return (TempX-x)*(TempX-x) + (TempY-y)*(TempY-y) <= DEST_RADIUS_SQUARED
endfunction

private function KillDests takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

//* Functions for knocking back adjacent units
private function KnockAdj_Check takes nothing returns boolean
    return TempUnit2 != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempUnit1)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Avul') <= 0
endfunction

//******************************************************************************
//* Some additional functions that can be used
function KnockbackStop takes unit targ returns boolean
    local integer id = GetUnitId(targ)
    set ToClear[id] = Entries[id]
    return ToClear[id] > 0
endfunction
function IsKnockedBack takes unit targ returns boolean
    return Entries[GetUnitId(targ)] > 0
endfunction

//* Struct for the system, I recommend leaving it alone
private struct knocker
    unit Source      = null
    unit Target      = null
    group HitGroup   = null
    effect KBEffect  = null
    integer FXMode   = 0
    boolean KillDest = false
    boolean KnockAdj = false
    boolean ChainAdj = false
    real Decrement   = 0.
    real Displace    = 0.
    real CosA        = 0.
    real SinA        = 0.
    
    public method checkterrain takes knocker n returns integer
        local real x = GetUnitX(n.Target)
        local real y = GetUnitY(n.Target)
        if IsTerrainPathingType(x, y, TERRAIN_PATHING_LAND) then
            return 1
        endif
        if IsTerrainPathingType(x, y, TERRAIN_PATHING_SHALLOW) then
            return 2
        endif
        return 0
    endmethod
    static method create takes unit source, unit targ, real angle, real disp, real dec, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns knocker
        local knocker n = knocker.allocate()
        set n.Target    = targ
        set n.Source    = source
        set n.FXMode    = n.checkterrain(n)
        set n.HitGroup  = NewGroup()
        set n.KillDest  = killDestructables
        set n.KnockAdj  = knockAdjacent
        set n.ChainAdj  = chainAdjacent
        set n.Decrement = dec
        set n.Displace  = disp
        set n.CosA      = Cos(angle)
        set n.SinA      = Sin(angle)
        
        if n.FXMode == 1 then
            set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
        elseif n.FXMode == 2 then
            set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
        debug else
            debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Create): Unknown Terrain Type")
        endif
        
        return n
    endmethod
    private method onDestroy takes nothing returns nothing
        local integer id = GetUnitId(this.Target)
        set Entries[id] = Entries[id] - 1
        if GetWidgetLife(this.Target) > 0.405 and Entries[id] <= 0 and ISSUE_LAST_ORDER then
            call IssueLastOrder(this.Target)
        endif
        call DestroyEffect(this.KBEffect)
        call ReleaseGroup(this.HitGroup)
    endmethod
endstruct

private function Update takes nothing returns nothing
    local unit u       = null
    local unit s       = null
    local rect r       = null
    local knocker n    = 0
    local knocker m    = 0
    local integer i    = Counter - 1
    local integer j    = 0
    local integer mode = 0
    local integer id   = 0
    local real xi      = 0.
    local real yi      = 0.
    local real xf      = 0.
    local real yf      = 0.
    
    loop
        exitwhen i < 0
        set n    = Knockers[i]
        set u    = n.Target
        set mode = n.FXMode
        set id   = GetUnitId(u)
        
        set xi   = GetUnitX(u)
        set yi   = GetUnitY(u)
        
        if n.Displace <= 0 or ToClear[id] > 0 then
            //* Clean up the knockback when it is over
            if ToClear[id] > 0 then
                set ToClear[id] = ToClear[id] - 1
            endif
            call n.destroy()
            set Counter = Counter - 1
            if Counter < 0 then
                call PauseTimer(Timer)
                set Counter = 0
            else
                set Knockers[i] = Knockers[Counter]
            endif
        else
            //* Propagate the knockback in space and time
            set xf = xi + n.Displace*n.CosA
            set yf = yi + n.Displace*n.SinA
            call SetUnitPosition(u, xf, yf)
            set n.FXMode = n.checkterrain(n)
            
            //* Modify the special effect if necessary
            if n.FXMode == 1 and mode == 2 then
                call DestroyEffect(n.KBEffect)
                set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
            elseif n.FXMode == 2 and mode == 1 then
                call DestroyEffect(n.KBEffect)
                set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
            debug elseif n.FXMode == 0 then
                debug call BJDebugMsg(SCOPE_PREFIX+" Error (In Update): Unknown Terrain Type")
            endif
            set n.Displace = n.Displace - n.Decrement
            
            //* Destroy destructables if desired
            if n.KillDest then
                set TempX = GetUnitX(u)
                set TempY = GetUnitY(u)
                call MoveRectTo(DestRect, TempX, TempY)
                call EnumDestructablesInRect(DestRect, DesBoolexpr, function KillDests)
            endif
            
            //* Knockback nearby units if desired
            if n.KnockAdj then
                set xi         = GetUnitX(u)
                set yi         = GetUnitY(u)
                set TempUnit1  = n.Source
                set TempUnit2  = u
                call GroupEnumUnitsInRange(ENUM_GROUP, xi, yi, ADJACENT_RADIUS, AdjBoolexpr)
                loop
                    set s = FirstOfGroup(ENUM_GROUP)
                    exitwhen s == null
                    if not IsUnitInGroup(s, n.HitGroup) then
                        set xf = GetUnitX(s)
                        set yf = GetUnitY(s)
                        call GroupAddUnit(n.HitGroup, s)
                        set m = knocker.create(n.Source, s, Atan2(yf-yi, xf-xi), n.Displace*ADJACENT_FACTOR, n.Decrement, n.KillDest, n.ChainAdj, n.ChainAdj)
                        call GroupAddUnit(m.HitGroup, u)
                        set Knockers[Counter] = m
                        set Counter           = Counter + 1
                    endif
                    call GroupRemoveUnit(ENUM_GROUP, s)
                endloop
            endif
        endif
        set i = i - 1
    endloop
    
    set u = null
    set s = null
endfunction

//******************************************************************************
//* How to knockback a unit
function KnockbackTarget takes unit source, unit targ, real angle, real startspeed, real decrement, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns boolean
    local knocker n  = 0
    local integer id = GetUnitId(targ)
    
    //* Protect users from themselves
    if decrement <= 0. or startspeed <= 0. or targ == null then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Call): Invalid Starting Conditions")
        return false
    endif
    //* Can't chain if you don't knockback adjacent units
    if not knockAdjacent and chainAdjacent then
        set chainAdjacent = false
    endif
    set n = knocker.create(source, targ, angle*bj_DEGTORAD, startspeed*TIMER_INTERVAL, decrement*TIMER_INTERVAL*TIMER_INTERVAL, killDestructables, knockAdjacent, chainAdjacent)
    if Counter == 0 then
        call TimerStart(Timer, TIMER_INTERVAL, true, function Update)
    endif
    
    set Entries[id]       = Entries[id] + 1
    set HitIndex[id]      = Counter + 1
    set Knockers[Counter] = n
    set Counter           = Counter + 1
    return true
endfunction

private function Init takes nothing returns nothing
    call SetRect(DestRect, -DEST_RADIUS, -DEST_RADIUS, DEST_RADIUS, DEST_RADIUS)
    set DesBoolexpr = Condition(function KillDests_Check)
    set AdjBoolexpr = Condition(function KnockAdj_Check)
endfunction
endlibrary

library LastOrder initializer Init needs UnitIndexingUtils
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library has a lot of usefulness for when you want to interface with the
//* last order a unit was given. This can be useful for simulating spell errors
//* and where you'd want to give them back the order they had prior to the spell
//* cast (whereas without this library, they'd just forget their orders).
//* 
//* There are some handy interfacing options for your use here --
//*     function GetLastOrderId takes unit u returns integer
//*     function GetLastOrderString takes unit u returns string
//*     function GetLastOrderType takes unit u returns integer
//*     function GetLastOrderX takes unit u returns real
//*     function GetLastOrderY takes unit u returns real
//*     function GetLastOrderTarget takes unit u returns widget
//*     function AbortOrder takes unit u returns boolean
//*
//* There are also some order commands that can be useful --
//*     function IssueLastOrder takes unit u returns boolean
//*     function IssueSecondLastOrder takes unit u returns boolean
//*     function IsLastOrderFinished takes unit u returns boolean
//* 
//* You can access any information you'd like about the orders for your own
//* order handling needs.
//* 
globals
    //* Storage for last order
    private          integer array Order
    private          integer array Type
    private          widget  array Targ
    private          boolean array Flag
    private          real    array X
    private          real    array Y
    
    //* Storage for second last order
    private          integer array P_Order
    private          integer array P_Type
    private          widget  array P_Targ
    private          boolean array P_Flag
    private          real    array P_X
    private          real    array P_Y
    
    //* Order type variables
            constant integer       ORDER_TYPE_TARGET    = 1
            constant integer       ORDER_TYPE_POINT     = 2
            constant integer       ORDER_TYPE_IMMEDIATE = 3
    
    //* Trigger for the order catching
    private          trigger       OrderTrg             = CreateTrigger()
endglobals

function GetUnitId takes handle h returns integer
    return GetHandleId(h)
endfunction

//**********************************************************
function GetLastOrderId takes unit u returns integer
    return Order[GetUnitId(u)]
endfunction
function GetLastOrderString takes unit u returns string
    return OrderId2String(Order[GetUnitId(u)])
endfunction
function GetLastOrderType takes unit u returns integer
    return Type[GetUnitId(u)]
endfunction
function GetLastOrderX takes unit u returns real
    return X[GetUnitId(u)]
endfunction
function GetLastOrderY takes unit u returns real
    return Y[GetUnitId(u)]
endfunction
function GetLastOrderTarget takes unit u returns widget
    return Targ[GetUnitId(u)]
endfunction
//**********************************************************
private function OrderExclusions takes unit u, integer id returns boolean
    //* Excludes specific orders or unit types from registering with the system
    //* 
    //* 851972: stop
    //*         Stop is excluded from the system, but you can change it by
    //*         adding a check for it below. id == 851972
    //* 
    //* 851971: smart
    //* 851986: move
    //* 851983: attack
    //* 851984: attackground
    //* 851990: patrol
    //* 851993: holdposition
    //*         These are the UI orders that are passed to the system.
    //* 
    //* >= 852055, <= 852762
    //*         These are all spell IDs from defend to incineratearrowoff with
    //*         a bit of leeway at the ends for orders with no strings.
    //* 
    return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction
private function LastOrderFilter takes unit u returns boolean
    //* Some criteria for whether or not a unit's last order should be given
    //* 
    //* INSTANT type orders are excluded because generally, reissuing an instant
    //* order doesn't make sense. You can remove that check below if you'd like,
    //* though.
    //* 
    //* The Type check is really just to ensure that no spell recursion can
    //* occur with IssueLastOrder. The problem with intercepting the spell cast
    //* event is that it happens after the order is 'caught' and registered to
    //* this system. Therefore, to just IssueLastOrder tells it to recast the
    //* spell! That's a problem, so we need a method to eliminate it.
    //* 
    local integer id = GetUnitId(u)
    return u != null and GetWidgetLife(u) > 0.405 and Type[id] != ORDER_TYPE_IMMEDIATE
endfunction
private function SecondLastOrderFilter takes unit u returns boolean
    //* Same as above but with regard to the second last order issued
    local integer id = GetUnitId(u)
    return u != null and GetWidgetLife(u) > 0.405 and P_Type[id] != ORDER_TYPE_IMMEDIATE and P_Order[id] != Order[id]
endfunction
//**********************************************************

function IsLastOrderFinished takes unit u returns boolean
    return (GetUnitCurrentOrder(u) == 0 and Order[GetUnitId(u)] != 851972) or Flag[GetUnitId(u)]
endfunction

function IssueLastOrder takes unit u returns boolean
    local integer id = GetUnitId(u)
    if LastOrderFilter(u) and Order[id] != 0 and not Flag[id] then
        if Type[id] == ORDER_TYPE_TARGET then
            return IssueTargetOrderById(u, Order[id], Targ[id])
        endif
        if Type[id] == ORDER_TYPE_POINT then
            return IssuePointOrderById(u, Order[id], X[id], Y[id])
        endif
        if Type[id] == ORDER_TYPE_IMMEDIATE then
            return IssueImmediateOrderById(u, Order[id])
        endif
    endif
    return false
endfunction

function IssueSecondLastOrder takes unit u returns boolean
    //* This function has to exist because of spell recursion
    local integer id = GetUnitId(u)
    if SecondLastOrderFilter(u) and P_Order[id] != 0 and not P_Flag[id] then
        if P_Type[id] == ORDER_TYPE_TARGET then
            return IssueTargetOrderById(u, P_Order[id], P_Targ[id])
        endif
        if P_Type[id] == ORDER_TYPE_POINT then
            return IssuePointOrderById(u, P_Order[id], P_X[id], P_Y[id])
        endif
        if P_Type[id] == ORDER_TYPE_IMMEDIATE then
            return IssueImmediateOrderById(u, P_Order[id])
        endif
    endif
    return false
endfunction

function AbortOrder takes unit u returns boolean
    if IsUnitPaused(u) then
        return false
    endif
    if not IsUnitPaused(u) then
        call PauseUnit(u, true)
        call IssueImmediateOrder(u, "stop")
        call PauseUnit(u, false)
    endif
    return true
endfunction
//**********************************************************

private function Conditions takes nothing returns boolean
    return OrderExclusions(GetTriggerUnit(), GetIssuedOrderId())
endfunction

private function Actions takes nothing returns nothing
    local unit    u  = GetTriggerUnit()
    local integer id = GetUnitId(u)
    
    //* Store second to last order to eliminate spell recursion
    set P_Order[id]  = Order[id]
    set P_Targ[id]   = Targ[id]
    set P_Type[id]   = Type[id]
    set P_Flag[id]   = Flag[id]
    set P_X[id]      = X[id]
    set P_Y[id]      = Y[id]
    
    set Flag[id]     = false
    set Order[id]    = GetIssuedOrderId()
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
        set Targ[id] = GetOrderTarget()
        set Type[id] = ORDER_TYPE_TARGET
        set X[id]    = GetWidgetX(GetOrderTarget())
        set Y[id]    = GetWidgetY(GetOrderTarget())
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
        set Targ[id] = null
        set Type[id] = ORDER_TYPE_POINT
        set X[id]    = GetOrderPointX()
        set Y[id]    = GetOrderPointY()
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
        set Targ[id] = null
        set Type[id] = ORDER_TYPE_IMMEDIATE
        set X[id]    = GetUnitX(u)
        set Y[id]    = GetUnitY(u)
    debug else
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Order Doesn't Exist")
    endif
    
    set u = null
endfunction
//**********************************************************

private function SpellActions takes nothing returns nothing
    set Flag[GetUnitId(GetTriggerUnit())] = true
endfunction
//**********************************************************

private function Init takes nothing returns nothing
    local trigger trg = CreateTrigger()
    call TriggerAddAction(OrderTrg, function Actions)
    call TriggerAddCondition(OrderTrg, Condition(function Conditions))
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
    
    call TriggerAddAction(trg, function SpellActions)
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    
    set trg = null
endfunction
endlibrary

library UnitIndexingUtils initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* -: BLUE FLAVOR :-
//* 
//* This can be used to index units with a unique integer for use with arrays
//* and things like that. This has a limit of 8191 indexes allocated at once in
//* terms of actually being usable in arrays. It won't give you an error if you
//* exceed 8191, but that is an unrealistic limit anyways.
//* 
//* The blue flavor uses a trigger that fires on death of a unit to release the
//* indexes of units. This is useful for maps where ressurection is not an issue
//* and doesn't require an O(n) search inside of a timer callback, making it
//* potentially less taxing on the game.
//* 
//* To use, call GetUnitId on a unit to retrieve its unique integer id. This
//* library allocates a unique index to a unit the instant it is created, which
//* means you can call GetUnitId immediately after creating the unit with no
//* worry.
//* 
//* Function Listing --
//*     function GetUnitId takes unit u returns integer
//* 
private struct unitindex
endstruct

//Function to get the unit's unique integer id, inlines to getting its userdata
private function GetUnitId takes unit u returns integer
    return GetUnitUserData(u)
endfunction

//Filter for units to index 
private function UnitFilter takes nothing returns boolean
    return true
endfunction

//Filter for what units to remove indexes for on death
private function Check takes nothing returns boolean
    return not IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO)
endfunction

private function Clear takes nothing returns nothing
    local unit u = GetTriggerUnit()
    call unitindex(GetUnitId(u)).destroy()
    call SetUnitUserData(u,-1)
    set u = null
endfunction

private function Add takes nothing returns boolean
    call SetUnitUserData(GetFilterUnit(),unitindex.create())
    return true
endfunction

private function GroupAdd takes nothing returns nothing
    call SetUnitUserData(GetEnumUnit(),unitindex.create())
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local region  r = CreateRegion()
    local group   g = CreateGroup()
    local integer i = 0
    
    //Use a filterfunc so units are indexed immediately
    call RegionAddRect(r, bj_mapInitialPlayableArea)
    call TriggerRegisterEnterRegion(t, r, And(Condition(function UnitFilter), Condition(function Add)))
    
    //Loop and group per player to grab all units, including those with locust
    loop
        exitwhen i > 15
        call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function UnitFilter))
        call ForGroup(g, function GroupAdd)
        set i = i + 1
    endloop
    
    //Set up the on-death trigger to clear custom values
    set t = CreateTrigger()
    call TriggerAddAction(t, function Clear)
    call TriggerAddCondition(t, Condition(function Check))
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    
    call DestroyGroup(g)
    set r = null
    set t = null
    set g = null
endfunction
endlibrary

library TerrainPathability initializer Initialization
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This can be used to detect the type of pathing a specific point of terrain
//* is, whether land, shallow water, or deep water. This type of detection
//* should have been easy to do using natives, but the IsTerrainPathable(...)
//* native is very counterintuitive and does not permit easy detection in one
//* call. For that reason, this library was developed.
//*
//* The system requires a dummy unit of some sort. There are no real
//* requirements upon the dummy unit, but it needs a non-zero movement speed.
//* More importantly than the dummy unit, though, it needs a custom windwalk
//* based unit ability with a 0.00 duration and no fade time. Both of those
//* raw id's (for the unit and windwalk dummy) need to be configured below.
//*
//* There is an objectmerger call available for those of you too lazy to build 
//* your own windwalk based ability. Simply uncomment it below and save once,
//* then close and reopen your map and recomment the line.
//*
globals
    constant integer TERRAIN_PATHING_DEEP                           = 1
    constant integer TERRAIN_PATHING_SHALLOW                        = 2
    constant integer TERRAIN_PATHING_LAND                           = 3
    constant integer TERRAIN_PATHING_WALKABLE                       = 4
    
    private unit Dummy                                              = null
    private constant integer DUMMY_UNIT_ID                          = 'hfoo'
    private constant integer DUMMY_WINDWALK_ID                      = 'win&'
    private constant player OWNING_PLAYER                           = Player(15)
    
    //* These variables shouldn't be adjusted
    private real WorldMinX                                          = 0.
    private real WorldMinY                                          = 0.
endglobals

////! external ObjectMerger w3a ANwk win& anam "Collision Ability" ansf "" Owk3 1 0.0 Owk4 1 0 Owk2 1 0.0 Owk1 1 0.0 acdn 1 0.0 ahdu 1 0.0 adur 1 0. aher 0 amcs 1 0

function IsTerrainPathingType takes real x, real y, integer terrainPathingType returns boolean
    local boolean b = false
    if terrainPathingType == TERRAIN_PATHING_DEEP then
        return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
    endif
    if terrainPathingType == TERRAIN_PATHING_SHALLOW then
        return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
    endif
    if terrainPathingType == TERRAIN_PATHING_LAND then
        return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
    endif
    if terrainPathingType == TERRAIN_PATHING_WALKABLE then
        call SetUnitPosition(Dummy, x, y)
        set b = GetUnitX(Dummy) == x and GetUnitY(Dummy) == y and not (not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY))
        call SetUnitX(Dummy, WorldMinX)
        call SetUnitY(Dummy, WorldMinY)
    endif
    return b
endfunction

private function Initialization takes nothing returns nothing
    local rect r = GetWorldBounds()
    
    set WorldMinX = GetRectMinX(r)
    set WorldMinY = GetRectMinY(r)
    set Dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, 0., 0., 0.)
    call UnitAddAbility(Dummy, DUMMY_WINDWALK_ID)
    call UnitAddAbility(Dummy, 'Avul')
    call IssueImmediateOrderById(Dummy, 852129)
    call SetUnitX(Dummy, WorldMinX)
    call SetUnitY(Dummy, WorldMinY)
    
    call RemoveRect(r)
    set r = null
endfunction
endlibrary

library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//*     function NewGroup takes nothing returns group
//*     function ReleaseGroup takes group g returns boolean
//*     function GroupRefresh takes group g returns nothing
//* 
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//* 
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//* 
globals
    //* Group for use with all instant enumerations
    group ENUM_GROUP = CreateGroup()
    
    //* Temporary references for GroupRefresh
    private boolean Flag                                              = false
    private group Refr                                                = null
    
    //* Assorted constants
    private constant integer MAX_HANDLE_COUNT                         = 408000
    private constant integer MIN_HANDLE_ID                            = 0x100000
    
    //* Arrays and counter for the group stack
    private group array Groups
    private integer array Status[MAX_HANDLE_COUNT]
    private integer Count                                             = 0
endglobals

private function H2I takes handle h returns integer
    return GetHandleId(h)
endfunction

private function AddEx takes nothing returns nothing
    if Flag then
        call GroupClear(Refr)
        set Flag = false
    endif
    call GroupAddUnit(Refr, GetEnumUnit())
endfunction
    
function GroupRefresh takes group g returns nothing
    set Flag = true
    set Refr = g
    call ForGroup(Refr, function AddEx)
    if Flag then
        call GroupClear(g)
    endif
endfunction

function NewGroup takes nothing returns group
    if Count == 0 then
        set Groups[0] = CreateGroup()
    else
        set Count = Count - 1
    endif
    set Status[H2I(Groups[Count])-MIN_HANDLE_ID] = 1
    return Groups[Count]
endfunction

function ReleaseGroup takes group g returns boolean
    local integer stat = Status[H2I(g)-MIN_HANDLE_ID]
    if g == null then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Null groups cannot be released")
        return false
    endif
    if stat == 0 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Group not part of stack")
        return false
    endif
    if stat == 2 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Groups cannot be multiply released")
        return false
    endif
    if Count == 8191 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Max groups achieved, destroying group")
        call DestroyGroup(g)
        return false
    endif
    if Count != 8191 then
        call GroupClear(g)
        set Groups[Count]                = g
        set Count                        = Count + 1
        set Status[H2I(g)-MIN_HANDLE_ID] = 2
    endif
    return true
endfunction
endlibrary

this code came with the spell, in a separate trigger...
Code:
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//* 
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//* 
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//* 
//* Constants that can be used from the library:
//*     [group]    ENUM_GROUP      As you might expect, this group is good for
//*                                when you need a group just for enumeration.
//*     [boolexpr] BOOLEXPR_TRUE   This is a true boolexpr, which is important
//*                                because a 'null' boolexpr in enumeration
//*                                calls results in a leak. Use this instead.
//*     [boolexpr] BOOLEXPR_FALSE  This exists mostly for completeness.
//* 
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//* 
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//* 
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//* 
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//* 
//* Example usage:
//*     local group MyGroup = NewGroup()
//*     call GroupRefresh(MyGroup)
//*     call ReleaseGroup(MyGroup)
//*     call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//*     call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//* 
globals
    //If you don't have xebasic in your map, this value will be used instead.
    //This value corresponds to the max collision size of a unit in your map.
    private constant real    MAX_COLLISION_SIZE = 197.
    //If you are insane and don't care about any of the protection involved in
    //this library, but want this script to be really fast, set this to true.
    private constant boolean LESS_SAFETY        = false
endglobals

globals
    //* Constants that are available to the user
    group    ENUM_GROUP     = CreateGroup()
    boolexpr BOOLEXPR_TRUE  = null
    boolexpr BOOLEXPR_FALSE = null
endglobals

globals
    //* Hashtable for debug purposes
    private hashtable     ht     = InitHashtable()
    //* Temporary references for GroupRefresh
    private boolean       Flag   = false
    private group         Refr   = null
    //* Arrays and counter for the group stack
    private group   array Groups
    private integer       Count  = 0
    //* Variables for use with the GroupUnitsInArea function
    private real          X      = 0.
    private real          Y      = 0.
    private real          R      = 0.
    private hashtable     H      = InitHashtable()
endglobals

private function HookDestroyGroup takes group g returns nothing
    if g == ENUM_GROUP then
        call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
    endif
endfunction

debug hook DestroyGroup HookDestroyGroup

private function AddEx takes nothing returns nothing
    if Flag then
        call GroupClear(Refr)
        set Flag = false
    endif
    call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
    set Flag = true
    set Refr = g
    call ForGroup(Refr, function AddEx)
    if Flag then
        call GroupClear(g)
    endif
endfunction

function NewGroup takes nothing returns group
    if Count == 0 then
        set Groups[0] = CreateGroup()
    else
        set Count = Count - 1
    endif
    static if not LESS_SAFETY then
        call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
    endif
    return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
    local integer id = GetHandleId(g)
    static if LESS_SAFETY then
        if g == null then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
            return false
        elseif Count == 8191 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
            call DestroyGroup(g)
            return false
        endif
    else
        if g == null then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
            return false
        elseif not HaveSavedInteger(ht, 0, id) then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
            return false
        elseif LoadInteger(ht, 0, id) == 2 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
            return false
        elseif Count == 8191 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
            call DestroyGroup(g)
            return false
        endif
        call SaveInteger(ht, 0, id, 2)
    endif
    call GroupClear(g)
    set Groups[Count] = g
    set Count         = Count + 1
    return true
endfunction

private function Filter takes nothing returns boolean
    return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction

private function HookDestroyBoolExpr takes boolexpr b returns nothing
    local integer bid = GetHandleId(b)
    if HaveSavedHandle(H, 0, bid) then
        //Clear the saved boolexpr
        call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
        call RemoveSavedHandle(H, 0, bid)
    endif
endfunction

hook DestroyBoolExpr HookDestroyBoolExpr

private constant function GetRadius takes real radius returns real
    static if LIBRARY_xebasic then
        return radius+XE_MAX_COLLISION_SIZE
    else
        return radius+MAX_COLLISION_SIZE
    endif
endfunction

function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
    local real    prevX = X
    local real    prevY = Y
    local real    prevR = R
    local integer bid   = 0
    
    //Set variables to new values
    set X = x
    set Y = y
    set R = radius
    if filter == null then
        //Adjusts for null boolexprs passed to the function
        set filter = Condition(function Filter)
    else
        //Check for a saved boolexpr
        set bid = GetHandleId(filter) 
        if HaveSavedHandle(H, 0, bid) then
            //Set the filter to use to the saved one
            set filter = LoadBooleanExprHandle(H, 0, bid)
        else
            //Create a new And() boolexpr for this filter
            set filter = And(Condition(function Filter), filter)
            call SaveBooleanExprHandle(H, 0, bid, filter)
        endif
    endif
    //Enumerate, if they want to use the boolexpr, this lets them
    call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
    //Give back original settings so nested enumerations work
    set X = prevX
    set Y = prevY
    set R = prevR
endfunction

function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
    local real prevX = X
    local real prevY = Y
    local real prevR = R
    
    //Set variables to new values
    set X = x
    set Y = y
    set R = radius
    //Enumerate
    call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
    //Give back original settings so nested enumerations work
    set X = prevX
    set Y = prevY
    set R = prevR
endfunction

private function True takes nothing returns boolean
    return true
endfunction
private function False takes nothing returns boolean
    return false
endfunction
private function Init takes nothing returns nothing
    set BOOLEXPR_TRUE  = Condition(function True)
    set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary

please help...
the error I get when saving is
library redeclared : GroupUtils
 

Laiev

Hey Listen!!
Reaction score
188
you don't need 2 library of the same type... if you already use it, just don't import it...
 

jig7c

Stop reading me...-statement
Reaction score
123
i tried that too... not importing the script that came with Energy Field, it gave me more errors..
This is what I just tried... in the knockback trigger, where it says
Code:
library Knockback initializer Init needs TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder

I made a new trigger for each, terrain, unitindexing and last order... and changed this
Code:
library Knockback initializer Init needs TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder
to
Code:
library Knockback initializer Init uses TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder
and kept the GroupUtils from the new map...

now I get different errors...


boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
these two lines are highlighted and I get multiply defined...

----edit-----
i just changed this
Code:
boolexpr BOOLEXPR_TRUE  = null
boolexpr BOOLEXPR_FALSE = null
to
Code:
//boolexpr BOOLEXPR_TRUE  = null
//boolexpr BOOLEXPR_FALSE = null

not sure if that is important.. im sure it is... any help?
 

Sevion

The DIY Ninja
Reaction score
413
If Energy Field is using it's own version of GroupUtils, then simply go through Energy Field and make it work using the version of GroupUtils you already have. Why is it using a non-standard version of GroupUtils anyhow?
 

jig7c

Stop reading me...-statement
Reaction score
123
If Energy Field is using it's own version of GroupUtils, then simply go through Energy Field and make it work using the version of GroupUtils you already have

dont know how to do that sevion....
 

Sevion

The DIY Ninja
Reaction score
413
Well depending on how its GroupUtils is integrated (integrated into the spell or separate library), they're modified differently.

If it's integrated directly into the spell, then you have to remove GroupUtil specific code and replace calls to them with calls to your GroupUtils.

If it's in a separate library, figure out if there are any differences and setup wrappers or whatever you need inside of GroupUtils (or be more standard and create a separate library with the wrappers inside of that).
 

jig7c

Stop reading me...-statement
Reaction score
123
i have no idea how to do that...

im post all the triggers related to the spell and EGUI..
it is 3 separate post due to character limit on the site....

here is the energy field spell
Code:
scope field initializer init

// Energy Field v1.4, by Dinowc
//        Credits to HoN

// Requirements:
// NewGen, Timer Utils, Group Utils, AIDS, dummy.mdx (everything included in map)

//How to import:
// Simply create a new trigger, convert it to custom text and replace everything with this code bellow. There are 3 abilities, 1 that casts the EF itself and other 2 are dummy abilities (Purge, Soul Burn). You can find them in Object editor.

//Configurables
globals
    private constant integer SPELL_ID = 'A03A'
    private constant integer PURGE_ID = 'A036'
    private constant integer SOUL_BURN_ID = 'A037'
    private constant integer DUMMY_CASTER = 'h009' // the dummy that casts Purge/Soul Burn
    private constant integer GADGET_DUMMY = 'h005' // self explanatory DMagnetic
    private constant integer TURRET_DUMMY= 'h00A' // same as above Turret
    private constant integer ARRAY_SIZE = 200
    private constant real INTERVAL = 0.05 // make it a multiplier of 1
    
    //Lightning Settings
    private constant string LIGHTNING_EFFECT = "CLSB" // since Wc3 doesn't have any pretty lightning effects and I do not want to use imports, I've decided to use this one
    private constant real Z1 = 10.
    private constant real Z2 = 50.
    private constant real Z3 = 90.
    private constant integer LIGHTNING1_DIRECTION = 0 // 0 for left, 1 for right, everything else doesn't create the lightning
    private constant integer LIGHTNING2_DIRECTION = 0 // 0 for left, 1 for right, everything else doesn't create the lightning
    private constant integer LIGHTNING3_DIRECTION = 0 // 0 for left, 1 for right, everything else doesn't create the lightning
    private constant boolean VISIBILITY_CHECK = true // I'm not sure what this means, but I left it at true xd
    
    //Energy Field Settings
    private constant real GADGET_OFFSET = 120. // distance between the caster and the gadget
    private constant real BOUNDS_SIZE = 50. // // distance between the lightning and entering unit before he get's damaged; it's not really accurate since the field is Xangled, not round
    private constant real TREE_CUT_AOE = 130. // AOE around turret where the destructables are destroyed
    private constant real BASE_DAMAGE = 0.03 // in percent
    private constant real DAMAGE_PER_LEVEL = 0.02 // in percent
    private constant integer TURRET_COUNT = 8 // less than 3 and more than 20 is not really recommended
    private constant real BASE_DURATION = 6.
    private constant real DURATION_PER_LEVEL = 3.
    private constant real BASE_AOE = 475.
    private constant real AOE_PER_LEVEL = 0.
    private constant integer BASE_HITS = 5
    private constant integer HITS_PER_LEVEL = 0
    private constant string BUILD_EFFECT = "Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl" // the effect that appears when you create the gadget, set to "" to display no effect (reduces lag)
    private constant string TURRET_EFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" // the effect that appears on turrets (obelisks), set to "" to display no effect (reduces lag)
    
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
    
    private constant boolean TURRET_COLLISION = true //if turret has collision
endglobals

//Don't touch
globals
    private rect R
    private unit DUMMY
    private group G = CreateGroup()
    private trigger Tr = CreateTrigger()
    private real RADIANS = 0.0174
endglobals

private function PolarX takes real dist, real angle returns real
    return dist * Cos(angle * RADIANS)
endfunction

private function PolarY takes real dist, real angle returns real
    return dist * Sin(angle * RADIANS)
endfunction

private function distance takes real x1, real y1, real x2, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function LevelIncrease takes real a, real b, integer level returns real
    return a + ((I2R(level - 1)) * b)
endfunction

private function GroupClean takes nothing returns nothing
    local unit picked = GetEnumUnit()
    local real x = GetUnitX(picked)
    local real y = GetUnitY(picked)
    
    call SetTerrainPathable(x, y, PATHING_TYPE_WALKABILITY, true)
    call KillUnit(picked)
    
    set picked = null
endfunction

private function destructableFilter takes nothing returns boolean
    local destructable d = GetFilterDestructable()
    
    if GetDestructableLife(d) > 0 then
        call KillDestructable(d)
    endif
    
    set d = null
    return false
endfunction

private function damage takes unit target, real amount returns real
    return GetUnitState(target, UNIT_STATE_MAX_LIFE) * amount
endfunction

private struct data
    unit caster
    unit gadget
    
    lightning array sfx[ARRAY_SIZE]
    integer index
    
    group g
    group g2
    
    real damagePercent
    real duration
    real ticks
    integer hits
    
    timer t
    
    static data D
    
    static method AddUnits takes nothing returns boolean
        local unit picked = GetFilterUnit()
        local player p = GetOwningPlayer(D.caster)

        if IsUnitEnemy(picked, p) == true and IsUnitInGroup(picked, D.g2) == false and IsUnitType(picked, UNIT_TYPE_DEAD) == false and GetUnitTypeId(picked) != GADGET_DUMMY and IsUnitType(picked, UNIT_TYPE_STRUCTURE) == false then
            call GroupAddUnit(D.g2, picked)
        endif
        
        set picked = null
        set p = null
        return false
    endmethod
    
    static method create takes unit whichUnit, real x, real y returns data
        local data d = data.allocate()
        local player p = GetOwningPlayer(whichUnit)
        local real angle = GetUnitFacing(whichUnit)
        local real x1 = x + PolarX(GADGET_OFFSET, angle)
        local real y1 = y + PolarY(GADGET_OFFSET, angle)
        local real x2
        local real y2
        local real angleInc = 360. / TURRET_COUNT
        local integer i = 0
        local integer level = GetUnitAbilityLevel(whichUnit, SPELL_ID)
        local real AOE = LevelIncrease(BASE_AOE, AOE_PER_LEVEL, level)
        local unit dummy
        local unit turret
        
        set d.caster = whichUnit
        set d.damagePercent = LevelIncrease(BASE_DAMAGE, DAMAGE_PER_LEVEL, level)
        set d.duration = LevelIncrease(BASE_DURATION, DURATION_PER_LEVEL, level)
        
        call MoveRectTo(R, x1, y1)
        call EnumDestructablesInRect(R, Filter(function destructableFilter), null)
        
        if BUILD_EFFECT != "" then
            set dummy = CreateUnit(p, DUMMY_CASTER, x1, y1, angle) // this dummy is used only as an effect for more eye candy, it's ok if you delete these 3 lines to reduce lag
            call DestroyEffect(AddSpecialEffectTarget(BUILD_EFFECT, dummy, "origin"))
            call KillUnit(dummy)
        endif
        
        set d.gadget = CreateUnit(p, GADGET_DUMMY, x1, y1, angle)
        set d.g = NewGroup()
        set d.g2 = NewGroup()
        
        set x = GetUnitX(d.gadget)
        set y = GetUnitY(d.gadget)
        
        loop
            set x1 = x + PolarX(AOE, angle)
            set y1 = y + PolarY(AOE, angle)
            
            call MoveRectTo(R, x1, y1)
            call EnumDestructablesInRect(R, Filter(function destructableFilter), null)
            
            if TURRET_EFFECT != "" then
                set dummy = CreateUnit(p, DUMMY_CASTER, x1, y1, angle) // this dummy is used only as an effect for more eye candy, it's ok if you delete these 3 lines to reduce lag
                call DestroyEffect(AddSpecialEffectTarget(TURRET_EFFECT, dummy, "origin"))
                call KillUnit(dummy)
            endif
            
            set turret = CreateUnit(p, TURRET_DUMMY, x1, y1, angle)
            
            if TURRET_COLLISION then
                call SetTerrainPathable(x1, y1, PATHING_TYPE_WALKABILITY, false)
            endif
            
            call GroupAddUnit(d.g, turret)
            
            set x2 = x + PolarX(AOE, angle + angleInc)
            set y2 = y + PolarY(AOE, angle + angleInc)
            
            if LIGHTNING1_DIRECTION == 0 then
                set d.sfx[d.index] = AddLightningEx(LIGHTNING_EFFECT, VISIBILITY_CHECK, x1, y1, Z1, x2, y2, Z1)
                set d.index = d.index + 1
            elseif LIGHTNING1_DIRECTION == 1 then
                set d.sfx[d.index] = AddLightningEx(LIGHTNING_EFFECT, VISIBILITY_CHECK, x2, y2, Z1, x1, y1, Z1)
                set d.index = d.index + 1
            endif
            
            if LIGHTNING2_DIRECTION == 0 then
                set d.sfx[d.index] = AddLightningEx(LIGHTNING_EFFECT, VISIBILITY_CHECK, x1, y1, Z2, x2, y2, Z2)
                set d.index = d.index + 1
            elseif LIGHTNING2_DIRECTION == 1 then
                set d.sfx[d.index] = AddLightningEx(LIGHTNING_EFFECT, VISIBILITY_CHECK, x2, y2, Z2, x1, y1, Z2)
                set d.index = d.index + 1
            endif
            
            if LIGHTNING3_DIRECTION == 0 then
                set d.sfx[d.index] = AddLightningEx(LIGHTNING_EFFECT, VISIBILITY_CHECK, x1, y1, Z3, x2, y2, Z3)
                set d.index = d.index + 1
            elseif LIGHTNING3_DIRECTION == 1 then
                set d.sfx[d.index] = AddLightningEx(LIGHTNING_EFFECT, VISIBILITY_CHECK, x2, y2, Z3, x1, y1, Z3)
                set d.index = d.index + 1
            endif
            
            set angle = angle + angleInc
            set i = i + 1
            
            exitwhen i >= TURRET_COUNT
        endloop
        
        set D = d
        call GroupEnumUnitsInRange(G, x, y, AOE - BOUNDS_SIZE, Filter(function data.AddUnits))
        call TriggerRegisterUnitEvent(Tr, d.gadget, EVENT_UNIT_DAMAGED)
        
        set d.ticks = 0.
        set d.hits = 0
        set d.t = NewTimer()
        
        set turret = null
        set dummy = null
        set p = null
        return d
    endmethod
    
    static method damageExiters takes nothing returns boolean
        local unit picked = GetFilterUnit()
        local player p = GetOwningPlayer(D.caster)
        local real x = GetUnitX(picked)
        local real y = GetUnitY(picked)
        local real dist = distance(x, y, GetUnitX(D.gadget), GetUnitY(D.gadget))
        local integer level = GetUnitAbilityLevel(D.caster, SPELL_ID)
        local real AOE = LevelIncrease(BASE_AOE, AOE_PER_LEVEL, level)
        
        if IsUnitEnemy(picked, p) == true and IsUnitInGroup(picked, D.g2) == true and IsUnitType(picked, UNIT_TYPE_DEAD) == false and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false and GetUnitTypeId(picked) != GADGET_DUMMY and IsUnitType(picked, UNIT_TYPE_STRUCTURE) == false and dist + BOUNDS_SIZE > AOE then
            call SetUnitOwner(DUMMY, p, false)
            call SetUnitX(DUMMY, x)
            call SetUnitY(DUMMY, y)
            
            call SetUnitAbilityLevel(DUMMY, PURGE_ID, level)
            call SetUnitAbilityLevel(DUMMY, SOUL_BURN_ID, level)
            call IssueTargetOrder(DUMMY, "purge", picked)
            call IssueTargetOrder(DUMMY, "soulburn", picked)
        
            call GroupRemoveUnit(D.g2, picked)
        endif
    
        set picked = null
        set p = null
        return false
    endmethod
    
    static method filterFunc takes nothing returns boolean
        local unit picked = GetFilterUnit()
        local player p = GetOwningPlayer(D.caster)
        local integer level
        
        if IsUnitEnemy(picked, p) == true and IsUnitType(picked, UNIT_TYPE_DEAD) == false and IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) == false and GetUnitTypeId(picked) != GADGET_DUMMY and IsUnitType(picked, UNIT_TYPE_STRUCTURE) == false then
            if IsUnitInGroup(picked, D.g2) == false then
                set level = GetUnitAbilityLevel(D.caster, SPELL_ID)
                
                call SetUnitOwner(DUMMY, p, false)
                call SetUnitX(DUMMY, GetUnitX(picked))
                call SetUnitY(DUMMY, GetUnitY(picked))
                
                call SetUnitAbilityLevel(DUMMY, PURGE_ID, level)
                call SetUnitAbilityLevel(DUMMY, SOUL_BURN_ID, level)
                call IssueTargetOrder(DUMMY, "purge", picked)
                call IssueTargetOrder(DUMMY, "soulburn", picked)
        
                call GroupAddUnit(D.g2, picked)
            endif
            
            if ModuloReal(D.ticks, 1) == 1 then
                call UnitDamageTarget(D.caster, picked, damage(picked, D.damagePercent), false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
                if TURRET_EFFECT != "" then
                    call DestroyEffect(AddSpecialEffectTarget(TURRET_EFFECT, picked, "origin"))
                endif
            endif
        endif
    
        set p = null
        set picked = null
        return false
    endmethod
    
    private method onDestroy takes nothing returns nothing
        local integer a = 0
        
        call ForGroup(.g, function GroupClean)
        call ReleaseGroup(.g)
        
        loop
            call DestroyLightning(.sfx[a])
            set .sfx[a] = null
            set a = a + 1
            exitwhen a > .index
        endloop
        set .index = 0
        
        call ReleaseGroup(.g2)
        call KillUnit(.gadget)
        call ReleaseTimer(.t)
    endmethod
endstruct

globals
    private data array STORE
endglobals

private function callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local data d = GetTimerData(t)
    local integer a = 0
    local real AOE = LevelIncrease(BASE_AOE, AOE_PER_LEVEL, (GetUnitAbilityLevel(d.caster, SPELL_ID)))
    set d.ticks = d.ticks + INTERVAL
    
    if d.ticks >= d.duration then
        call d.destroy()
    else
        set data.D = d
        call GroupEnumUnitsInRange(G, GetUnitX(d.gadget), GetUnitY(d.gadget), AOE - BOUNDS_SIZE, function data.filterFunc)
        call GroupEnumUnitsInRange(G, GetUnitX(d.gadget), GetUnitY(d.gadget), AOE + BOUNDS_SIZE, function data.damageExiters)
    endif
    
    set t = null
endfunction

private function actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local real x = GetUnitX(caster)
    local real y = GetUnitY(caster)
    local data d = data.create(caster, x, y)
    
    set STORE[GetUnitIndex(d.gadget)] = d
    call SetTimerData(d.t, d)
    call TimerStart(d.t, INTERVAL, true, function callback)
    
    set caster = null
endfunction

private function conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

private function act takes nothing returns boolean
    local unit attacked = GetTriggerUnit()
    local data d
    local integer a
    local integer HITS
    set d = STORE[GetUnitIndex(attacked)]
        
    if d != null then
        set HITS = BASE_HITS + ((GetUnitAbilityLevel(d.caster, SPELL_ID) - 1) * HITS_PER_LEVEL)
        set d.hits = d.hits + 1
        
        if d.hits >= HITS then
            call d.destroy()
        else
            set STORE[GetUnitIndex(attacked)] = d
        endif
    endif
    
    set attacked = null
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local real aoe = TREE_CUT_AOE / 2
    
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function conditions))
    call TriggerAddAction(t, function actions)
    call TriggerAddCondition(Tr, Condition(function act))
    
    set R = Rect(-1 * aoe, -1 * aoe, aoe, aoe)
    
    //Preload
    call RemoveUnit(CreateUnit(Player(13), GADGET_DUMMY, 0., 0., 0.))
    call RemoveUnit(CreateUnit(Player(13), TURRET_DUMMY, 0., 0., 0.))
    set DUMMY = CreateUnit(Player(13), DUMMY_CASTER, 0., 0., 0.)
    
    set t = null
endfunction

endscope

here is the grouputil that came with it...

Code:
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//* 
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//* 
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//* 
//* Constants that can be used from the library:
//*     [group]    ENUM_GROUP      As you might expect, this group is good for
//*                                when you need a group just for enumeration.
//*     [boolexpr] BOOLEXPR_TRUE   This is a true boolexpr, which is important
//*                                because a 'null' boolexpr in enumeration
//*                                calls results in a leak. Use this instead.
//*     [boolexpr] BOOLEXPR_FALSE  This exists mostly for completeness.
//* 
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//* 
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//* 
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//* 
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//* 
//* Example usage:
//*     local group MyGroup = NewGroup()
//*     call GroupRefresh(MyGroup)
//*     call ReleaseGroup(MyGroup)
//*     call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//*     call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//* 
globals
    //If you don't have xebasic in your map, this value will be used instead.
    //This value corresponds to the max collision size of a unit in your map.
    private constant real    MAX_COLLISION_SIZE = 197.
    //If you are insane and don't care about any of the protection involved in
    //this library, but want this script to be really fast, set this to true.
    private constant boolean LESS_SAFETY        = false
endglobals

globals
    //* Constants that are available to the user
    group    ENUM_GROUP     = CreateGroup()
   // boolexpr BOOLEXPR_TRUE  = null
   // boolexpr BOOLEXPR_FALSE = null
endglobals

globals
    //* Hashtable for debug purposes
    private hashtable     ht     = InitHashtable()
    //* Temporary references for GroupRefresh
    private boolean       Flag   = false
    private group         Refr   = null
    //* Arrays and counter for the group stack
    private group   array Groups
    private integer       Count  = 0
    //* Variables for use with the GroupUnitsInArea function
    private real          X      = 0.
    private real          Y      = 0.
    private real          R      = 0.
    private hashtable     H      = InitHashtable()
endglobals

private function HookDestroyGroup takes group g returns nothing
    if g == ENUM_GROUP then
        call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
    endif
endfunction

debug hook DestroyGroup HookDestroyGroup

private function AddEx takes nothing returns nothing
    if Flag then
        call GroupClear(Refr)
        set Flag = false
    endif
    call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
    set Flag = true
    set Refr = g
    call ForGroup(Refr, function AddEx)
    if Flag then
        call GroupClear(g)
    endif
endfunction

function NewGroup takes nothing returns group
    if Count == 0 then
        set Groups[0] = CreateGroup()
    else
        set Count = Count - 1
    endif
    static if not LESS_SAFETY then
        call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
    endif
    return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
    local integer id = GetHandleId(g)
    static if LESS_SAFETY then
        if g == null then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
            return false
        elseif Count == 8191 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
            call DestroyGroup(g)
            return false
        endif
    else
        if g == null then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
            return false
        elseif not HaveSavedInteger(ht, 0, id) then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
            return false
        elseif LoadInteger(ht, 0, id) == 2 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
            return false
        elseif Count == 8191 then
            debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
            call DestroyGroup(g)
            return false
        endif
        call SaveInteger(ht, 0, id, 2)
    endif
    call GroupClear(g)
    set Groups[Count] = g
    set Count         = Count + 1
    return true
endfunction

private function Filter takes nothing returns boolean
    return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction

private function HookDestroyBoolExpr takes boolexpr b returns nothing
    local integer bid = GetHandleId(b)
    if HaveSavedHandle(H, 0, bid) then
        //Clear the saved boolexpr
        call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
        call RemoveSavedHandle(H, 0, bid)
    endif
endfunction

hook DestroyBoolExpr HookDestroyBoolExpr

private constant function GetRadius takes real radius returns real
    static if LIBRARY_xebasic then
        return radius+XE_MAX_COLLISION_SIZE
    else
        return radius+MAX_COLLISION_SIZE
    endif
endfunction

function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
    local real    prevX = X
    local real    prevY = Y
    local real    prevR = R
    local integer bid   = 0
    
    //Set variables to new values
    set X = x
    set Y = y
    set R = radius
    if filter == null then
        //Adjusts for null boolexprs passed to the function
        set filter = Condition(function Filter)
    else
        //Check for a saved boolexpr
        set bid = GetHandleId(filter) 
        if HaveSavedHandle(H, 0, bid) then
            //Set the filter to use to the saved one
            set filter = LoadBooleanExprHandle(H, 0, bid)
        else
            //Create a new And() boolexpr for this filter
            set filter = And(Condition(function Filter), filter)
            call SaveBooleanExprHandle(H, 0, bid, filter)
        endif
    endif
    //Enumerate, if they want to use the boolexpr, this lets them
    call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
    //Give back original settings so nested enumerations work
    set X = prevX
    set Y = prevY
    set R = prevR
endfunction

function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
    local real prevX = X
    local real prevY = Y
    local real prevR = R
    
    //Set variables to new values
    set X = x
    set Y = y
    set R = radius
    //Enumerate
    call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
    //Give back original settings so nested enumerations work
    set X = prevX
    set Y = prevY
    set R = prevR
endfunction

private function True takes nothing returns boolean
    return true
endfunction
private function False takes nothing returns boolean
    return false
endfunction
private function Init takes nothing returns nothing
    set BOOLEXPR_TRUE  = Condition(function True)
    set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
 

jig7c

Stop reading me...-statement
Reaction score
123
it uses AID, which is right here
Code:
//  
//        _   ___ ___  ___    _______________________________________________
//       /_\ |_ _|   \/ __|   ||     A D V A N C E D   I N D E X I N G     ||
//      / _ \ | || |) \__ \   ||                  A N D                    ||
//     /_/ \_\___|___/|___/   ||         D A T A   S T O R A G E           ||
//            By Jesus4Lyf    ———————————————————————————————————————————————
//                                                                    v 1.1.0
//      What is AIDS?
//     ———————————————
//          AIDS assigns unique integers between 1 and 8191 to units which enter
//          the map. These can be used for arrays and data attaching.
//          
//          AIDS also allows you to define structs which are created automatically
//          when units enter the map, and filtering which units should be indexed
//          as well as for which units these structs should be created.
//          
//      How to implement?
//     ———————————————————
//          Simply create a new trigger object called AIDS, go to 'Edit -> Convert
//          to Custom Text', and replace everything that's there with this script.
//
//          Save the map, close it, reopen it, and then delete the "!" from the
//          FAR left side of the next lines (so "external" will line up with this line):
//!          external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
//          At the top of the script, there is a 'UnitIndexingFilter' constant
//          function. If the function returns true for the unit, then that unit
//          will be automatically indexed. Setting this to true will automatically
//          index all units. Setting it to false will disable automatic indexing.
//
//      Functions:
//     ————————————
//          function GetUnitId takes unit u returns integer
//              - This returns the index of an indexed unit. This will return 0
//                if the unit has not been indexed.
//              - This function inlines. It does not check if the unit needs an
//                index. This function is for the speed freaks.
//              - Always use this if 'UnitIndexingFilter' simply returns true.
//
//          function GetUnitIndex takes unit u returns integer
//              - This will return the index of a unit if it has one, or assign
//                an index if the unit doesn't have one (and return the new index).
//              - Use this if 'UnitIndexingFilter' doesn't return true.
//
//          function GetIndexUnit takes integer index returns unit
//              - This returns the unit which has been assigned the 'index'.
//
//      AIDS Structs:
//     ——————————————
//          - Insert: //! runtextmacro AIDS() at the top of a struct to make it
//            an AIDS struct.
//          - AIDS structs cannot be created or destroyed manually. Instead, they
//            are automatically created when an appropriate unit enters the map.
//          - You cannot give members default values in their declaration.
//            (eg: private integer i=5 is not allowed)
//          - You cannot use array members.
//          - AIDS structs must "extend array". This will remove some unused
//            functions and enforce the above so there will be no mistakes.
//          - There are four optional methods you can use in AIDS structs:
//              - AIDS_onCreate takes nothing returns nothing
//                  - This is called when the struct is 'created' for the unit.
//                  - In here you can assign members their default values, which
//                    you would usually assign in their declarations.
//                    (eg: set this.i=5)
//              - AIDS_onDestroy takes nothing returns nothing
//                  - This is called when the struct is 'destroyed' for the unit.
//                  - This is your substitute to the normal onDestroy method.
//              - AIDS_filter takes unit u returns boolean
//                  - This is similar to the constant filter in the main system.
//                  - Each unit that enters the map will be tested by each AIDS
//                    struct filter. If it returns true for that unit, that unit
//                    will be indexed if it was not already, the AIDS struct will
//                    have its AIDS_onCreate method called, and later have its
//                    AIDS_onDestroy method called when the index is recycled.
//                  - Not declaring this will use the default AIDS filter instead.
//              - AIDS_onInit takes nothing returns nothing
//                  - This is because I stole your onInit function with my textmacro.
//          - You can use '.unit' from any AIDS struct to get the unit for which
//            the struct is for.
//          - The structs id will be the units index, so getting the struct for
//            a unit inlines to a single native call, and you can typecast between
//            different AIDS structs. This is the premise of AIDS.
//          - Never create or destroy AIDS structs directly.
//          - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
//            decrease the lock level on the struct. If a struct's lock level is
//            not 0, it will not be destroyed until it is reduced to 0. Locks just
//            put off AIDS struct destruction in case you wish to attach to a timer
//            or something which must expire before the struct data disappears.
//            Hence, not freeing all locks will leak the struct (and index).
//
//      PUI and AutoIndex:
//     ————————————————————
//          - AIDS includes the PUI textmacros and the AutoIndex module, because
//            these systems are not compatible with AIDS but have valid and distinct
//            uses.
//          - The PUI textmacros are better to use for spells than AIDS structs,
//            because they are not created for all units, just those targetted by
//            the spell (or whatever else is necessary).
//          - The AutoData module is good for very simple array syntax for data
//            attachment (although I don't recommend that people actually use it,
//            it's here mostly for compatability). Note that unlike the PUI textmacros,
//            units must pass the AIDS filter in order for this module to work with
//            them. This is exactly as the same as in AutoIndex itself (AutoIndex
//            has a filter too).
//          
//      Thanks:
//     —————————
//          - Romek, for writing 90% of this user documentation, challenging my
//            interface, doing some testing, suggesting improvements and inspiring
//            me to re-do my code to include GetUnitIndex as non-inlining.
//          - grim001, for writing the AutoData module, and AutoIndex. I used the
//            on-enter-map method that he used. Full credits for the AutoData module.
//          - Cohadar, for writing his PUI textmacros. Full credits to him for these,
//            except for my slight optimisations for this system.
//            Also, I have used an optimised version of his PeriodicRecycler from
//            PUI in this system to avoid needing a RemoveUnitEx function.
//          - Vexorian, for helping Cohadar on the PUI textmacro.
//          - Larcenist, for suggesting the AIDS acronym. Originally he suggested
//            'Alternative Index Detection System', but obviously I came up with
//            something better. In fact, I'd say it looks like the acronym was
//            an accident. Kinda neat, don't you think? :P
//
//      Final Notes:
//     ——————————————
//          - With most systems, I wouldn't usually add substitutes for alternative
//            systems. However, UnitData systems are an exception, because they
//            are incompatible with eachother. Since using this system forbids
//            people from using the real PUI or AutoIndex, and a lot of resources
//            use either of these, it made sense not to break them all.
//
//          - If this documentation confused you as to how to use the system, just
//            leave everything as default and use GetUnitId everywhere.
//
//          - To use this like PUI (if you don't like spamming indices) simply
//            make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS
    //==============================================================================
    // Configurables
    //
    globals
        private constant boolean USE_PERIODIC_RECYCLER = false
        private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
                                               // Lower to be able to recycle faster.
                                               // Only used if USE_PERIODIC_RECYCLER
                                               // is set to true.
        
        private constant integer LEAVE_DETECTION_ABILITY = 'A039'
    endglobals
    
    private function UnitIndexingFilter takes unit u returns boolean
        return false
    endfunction
    
    //==============================================================================
    // System code
    //
    globals
        // The unit stored at an index.
        private unit array IndexUnit
        private integer array LockLevel
    endglobals
    
    //==============================================================================
    globals
        // Recycle stack
        private integer array RecycledIndex
        private integer MaxRecycledIndex = 0
        
        // Previous highest index
        private integer MaxIndex = 0
    endglobals
    
    //==============================================================================
    globals
        private integer array DecayingIndex
        private integer MaxDecayingIndex=0
        private integer DecayChecker=0
    endglobals
    
    globals
        private timer UndefendTimer=CreateTimer()
        private integer array UndefendIndex
        private integer UndefendStackIndex=0
    endglobals
    globals
        private integer array UndefendExpiringIndex
        private integer UndefendExpiringIndexLevel=0
    endglobals
    
    //==============================================================================
    globals
        // The Add/Remove stack (or assign/recycle stack).
        // 
        // Indexing can become recusive since units can be created on index
        // assignment or deallocation.
        // To support this, a stack is used to store the event response results.
        private integer ARStackLevel=0
        private integer array ARStackIndex
        private unit    array ARStackUnit
        
        // A later discovery revealed that the Add/Remove stack did not need to be
        // used for deallocation. The alternative used works fine...
    endglobals
    
    public constant function GetEnteringIndexUnit takes nothing returns unit
        return ARStackUnit[ARStackLevel]
    endfunction
    
    public function GetIndexOfEnteringUnit takes nothing returns integer
        // Called in AIDS structs when units do not pass the initial AIDS filter.
        
        if ARStackIndex[ARStackLevel]==0 then
            // Get new index, from recycler first, else new.
            // Store the current index on the (new) top level of the AR stack.
            if MaxRecycledIndex==0 then // Get new.
                set MaxIndex=MaxIndex+1
                set ARStackIndex[ARStackLevel]=MaxIndex
            else // Get from recycle stack.
                set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
                set MaxRecycledIndex=MaxRecycledIndex-1
            endif
            
            // Store index on unit.
            call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
            set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
            
            // Add index to recycle list.
            set MaxDecayingIndex=MaxDecayingIndex+1
            set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
        endif
        
        return ARStackIndex[ARStackLevel]
    endfunction
    
    public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
        // Called in AIDS structs when units have passed the initial AIDS filter.
        return ARStackIndex[ARStackLevel]
    endfunction
    public constant function GetDecayingIndex takes nothing returns integer
        static if USE_PERIODIC_RECYCLER then
            return DecayingIndex[DecayChecker]
        else
            return UndefendExpiringIndex[UndefendExpiringIndexLevel]
        endif
    endfunction
    
    //==============================================================================
    globals
        // For structs and such which need to do things on unit index assignment.
        private trigger OnEnter=CreateTrigger()
        // The same, but for when units pass the initial filter anyway.
        private trigger OnEnterAllocated=CreateTrigger()
        // For structs and such which need to do things on unit index deallocation.
        private trigger OnDeallocate=CreateTrigger()
    endglobals
    
    public function RegisterOnEnter takes boolexpr b returns triggercondition
        return TriggerAddCondition(OnEnter,b)
    endfunction
    public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
        return TriggerAddCondition(OnEnterAllocated,b)
    endfunction
    public function RegisterOnDeallocate takes boolexpr b returns triggercondition
        return TriggerAddCondition(OnDeallocate,b)
    endfunction
    
    //==============================================================================
    function GetIndexUnit takes integer index returns unit
        debug if index==0 then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
        debug elseif IndexUnit[index]==null then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
        debug endif
        
        return IndexUnit[index]
    endfunction
    
    function GetUnitId takes unit u returns integer
        debug if u==null then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
        debug elseif GetUnitUserData(u)==0 then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
        debug endif
        
        return GetUnitUserData(u)
    endfunction
    
    globals//locals
        private integer getindex
    endglobals
    function GetUnitIndex takes unit u returns integer // Cannot be recursive.
        debug if u==null then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
        debug endif
        
        set getindex=GetUnitUserData(u)
        
        if getindex==0 then
            // Get new index, from recycler first, else new.
            // Store the current index in getindex.
            if MaxRecycledIndex==0 then // Get new.
                set MaxIndex=MaxIndex+1
                set getindex=MaxIndex
            else // Get from recycle stack.
                set getindex=RecycledIndex[MaxRecycledIndex]
                set MaxRecycledIndex=MaxRecycledIndex-1
            endif
            
            // Store index on unit.
            call SetUnitUserData(u,getindex)
            set IndexUnit[getindex]=u
            
            static if USE_PERIODIC_RECYCLER then
                
                // Add index to recycle list.
                set MaxDecayingIndex=MaxDecayingIndex+1
                set DecayingIndex[MaxDecayingIndex]=getindex
                
            else
            
                // Add leave detection ability.
                call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
                call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
                
            endif
            
            // Do not fire things here. No AIDS structs will be made at this point.
        endif
        
        return getindex
    endfunction
    
    //==============================================================================
    public function AddLock takes integer index returns nothing
        set LockLevel[index]=LockLevel[index]+1
    endfunction
    public function RemoveLock takes integer index returns nothing
        set LockLevel[index]=LockLevel[index]-1
        
        static if not USE_PERIODIC_RECYCLER then
            if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
                
                // Increment stack for recursion.
                set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
                set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
                
                // Fire things.
                call TriggerEvaluate(OnDeallocate)
                
                // Decrement stack for recursion.
                set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
                
                // Add the index to the recycler stack.
                set MaxRecycledIndex=MaxRecycledIndex+1
                set RecycledIndex[MaxRecycledIndex]=index
                
                // Null the unit.
                set IndexUnit[index]=null
                
            endif
        endif
    endfunction
    
    //==============================================================================
    static if USE_PERIODIC_RECYCLER then
        
        private function PeriodicRecycler takes nothing returns nothing
            if MaxDecayingIndex>0 then
                set DecayChecker=DecayChecker+1
                if DecayChecker>MaxDecayingIndex then
                    set DecayChecker=1
                endif
                if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
                if LockLevel[DecayingIndex[DecayChecker]]==0 then
                    
                    // Fire things.
                    call TriggerEvaluate(OnDeallocate)
                    
                    // Add the index to the recycler stack.
                    set MaxRecycledIndex=MaxRecycledIndex+1
                    set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
                    
                    // Null the unit.
                    set IndexUnit[DecayingIndex[DecayChecker]]=null
                    
                    // Remove index from decay list.
                    set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
                    set MaxDecayingIndex=MaxDecayingIndex-1
                    
                endif
                endif
            endif
        endfunction
        
    else
        
        private function UndefendFilter takes nothing returns boolean
            return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
        endfunction
        
        private function OnUndefendTimer takes nothing returns nothing
            loop
                exitwhen UndefendStackIndex==0
                
                set UndefendStackIndex=UndefendStackIndex-1
                set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
                
                if IndexUnit[UndefendExpiringIndex[0]]!=null then
                if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
                if LockLevel[UndefendExpiringIndex[0]]==0 then
                    
                    // Fire things.
                    call TriggerEvaluate(OnDeallocate)
                    
                    // Add the index to the recycler stack.
                    set MaxRecycledIndex=MaxRecycledIndex+1
                    set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
                    
                    // Null the unit.
                    set IndexUnit[UndefendExpiringIndex[0]]=null
                    
                endif
                endif
                endif
                
            endloop
        endfunction
        
        globals//locals
            private integer UndefendFilterIndex
        endglobals
        private function OnUndefend takes nothing returns boolean
            if GetIssuedOrderId()==852056 then // If undefend then...
                set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
                
                if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
                    set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
                    set UndefendStackIndex=UndefendStackIndex+1
                    
                    call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
                endif
            endif
            
            return false
        endfunction
        
    endif
    
    //==============================================================================
    public function IndexEnum takes nothing returns boolean // Can be recursive...
        // Start by adding another level on the AR stack (for recursion's sake).
        set ARStackLevel=ARStackLevel+1
        
        // Store the current unit on the (new) top level of the AR stack.
        set ARStackUnit[ARStackLevel]=GetFilterUnit()
        
        if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
            
            if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
                
                // Get new index, from recycler first, else new.
                // Store the current index on the (new) top level of the AR stack.
                if MaxRecycledIndex==0 then // Get new.
                    set MaxIndex=MaxIndex+1
                    set ARStackIndex[ARStackLevel]=MaxIndex
                else // Get from recycle stack.
                    set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
                    set MaxRecycledIndex=MaxRecycledIndex-1
                endif
                
                // Store index on unit.
                call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
                set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
                
                static if USE_PERIODIC_RECYCLER then
                    
                    // Add index to recycle list.
                    set MaxDecayingIndex=MaxDecayingIndex+1
                    set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
                    
                else
                    
                    // Add leave detection ability.
                    call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
                    call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
                    
                endif
                
                // Fire things.
                call TriggerEvaluate(OnEnter)
                
            else
                
                // The unit did not pass the filters, so does not need to be auto indexed.
                // However, for certain AIDS structs, it may still require indexing.
                // These structs may index the unit on their creation.
                // We flag that an index must be assigned by setting the current index to 0.
                set ARStackIndex[ARStackLevel]=0
                
                // Fire things.
                call TriggerEvaluate(OnEnter)
                
            endif
            
        endif
        
        // Decrement the stack.
        set ARStackLevel=ARStackLevel-1
        
        return false
    endfunction
    
    //==============================================================================
    private function InitAIDS takes nothing returns nothing
        local region r=CreateRegion()
        
        local group g=CreateGroup()
        local integer n=15
        
        static if USE_PERIODIC_RECYCLER then
            
            call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
            
        else
            
            local trigger t=CreateTrigger()
            
            loop
                call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
                call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
                // Capture "undefend" orders.
                exitwhen n==0
                set n=n-1
            endloop
            set n=15
            
            call TriggerAddCondition(t,Filter(function OnUndefend))
            set t=null
            
        endif
        
        // This must be done first, due to recursion. :)
        call RegionAddRect(r,GetWorldBounds())
        call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
        set r=null
        
        loop
            call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
            //Enum every non-filtered unit on the map during initialization and assign it a unique
            //index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
            exitwhen n==0
            set n=n-1
        endloop
        call DestroyGroup(g)
        set g=null
    endfunction
    
    //==============================================================================
    public struct DEFAULT extends array
        method AIDS_onCreate takes nothing returns nothing
        endmethod
        method AIDS_onDestroy takes nothing returns nothing
        endmethod
        
        static method AIDS_filter takes unit u returns boolean
            return UnitIndexingFilter(u)
        endmethod
        
        static method AIDS_onInit takes nothing returns nothing
        endmethod
    endstruct
    
    //===========================================================================
    //  Never create or destroy AIDS structs directly.
    //  Also, do not initialise members except by using the AIDS_onCreate method.
    //===========================================================================
    //! textmacro AIDS
        // This magic line makes default methods get called which do nothing
        // if the methods are otherwise undefined.
        private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
        
        //-----------------------------------------------------------------------
        // Gotta know whether or not to destroy on deallocation...
        private boolean AIDS_instanciated
        
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns thistype
            return GetUnitId(whichUnit)
        endmethod
        
        method operator unit takes nothing returns unit
            // Allows structVar.unit to return the unit.
            return GetIndexUnit(this)
        endmethod
        
        //-----------------------------------------------------------------------
        method AIDS_addLock takes nothing returns nothing
            call AIDS_AddLock(this)
        endmethod
        method AIDS_removeLock takes nothing returns nothing
            call AIDS_RemoveLock(this)
        endmethod
        
        //-----------------------------------------------------------------------
        private static method AIDS_onEnter takes nothing returns boolean
            // At this point, the unit might not have been assigned an index.
            if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
                // Flag it for destruction on deallocation.
                set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
                // Can use inlining "Assigned" function now, as it must be assigned.
                call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
            endif
            
            return false
        endmethod
        
        private static method AIDS_onEnterAllocated takes nothing returns boolean
            // At this point, the unit must have been assigned an index.
            if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
                // Flag it for destruction on deallocation. Slightly faster!
                set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
                // Can use inlining "Assigned" function now, as it must be assigned.
                call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
            endif
            
            return false
        endmethod
        
        private static method AIDS_onDeallocate takes nothing returns boolean
            if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
                call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
                // Unflag destruction on deallocation.
                set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
            endif
            
            return false
        endmethod
        
        //-----------------------------------------------------------------------
        private static method onInit takes nothing returns nothing
            call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
            call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
            call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
            
            // Because I robbed you of your struct's onInit method.
            call thistype.AIDS_onInit()
        endmethod
    //! endtextmacro
endlibrary

library PUI uses AIDS
    //===========================================================================
    //  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
    //  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
    //  Instead put them in a struct and use PUI textmacro
    //===========================================================================
    //! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
    $VISIBILITY$ struct $NAME$
        private static unit   array pui_unit
        private static $TYPE$ array pui_data
        
        //-----------------------------------------------------------------------
        //  Returns default value when first time used
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns $TYPE$
            local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
            if .pui_unit[pui] != whichUnit then
                set .pui_unit[pui] = whichUnit
                set .pui_data[pui] = $DEFAULT$
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
        endmethod
    endstruct
    //! endtextmacro

    //===========================================================================
    //  Never destroy PUI structs directly.
    //  Use .release() instead, will call .destroy()
    //===========================================================================
    //! textmacro PUI
        private static unit    array pui_unit
        private static integer array pui_data
        private static integer array pui_id
        
        //-----------------------------------------------------------------------
        //  Returns zero if no struct is attached to unit
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns integer
            local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
            // Switched the next two lines for optimisation.
            if .pui_unit[pui] != whichUnit then
                if .pui_data[pui] != 0 then
                    // recycled index detected
                    call .destroy(.pui_data[pui])
                    set .pui_unit[pui] = null
                    set .pui_data[pui] = 0            
                endif
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        //  This will overwrite already attached struct if any
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, integer whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                call .destroy(.pui_data[pui])
            endif
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
            set .pui_id[whichData] = pui
        endmethod

        //-----------------------------------------------------------------------
        //  If you do not call release struct will be destroyed when unit handle gets recycled
        //-----------------------------------------------------------------------
        method release takes nothing returns nothing
            local integer pui= .pui_id[integer(this)]
            call .destroy()
            set .pui_unit[pui] = null
            set .pui_data[pui] = 0
        endmethod
    //! endtextmacro
endlibrary

library AutoIndex uses AIDS
    module AutoData
        private static thistype array data
        
        // Fixed up the below to use thsitype instead of integer.
        static method operator []= takes unit u, thistype i returns nothing
            set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
        endmethod                       //using the module's thistype array.
        
        static method operator [] takes unit u returns thistype
            return .data[GetUnitId(u)] //Just returning the attached struct.
        endmethod
    endmodule
endlibrary
 

jig7c

Stop reading me...-statement
Reaction score
123
and finally here is the knockback trigger that came with EGUI... groupUtil is towards the end of the script...
Code:
//******************************************************************************
//*                                                                            *
//*                             K N O C K B A C K                              *
//*                                Actual Code                                 *
//*                                   v1.06                                    *
//*                                                                            *
//*                              By: Rising_Dusk                               *
//*                                                                            *
//******************************************************************************

library Knockback initializer Init needs TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder
globals
    //*********************************************************
    //* These are the configuration constants for the system
    //*
    //* EFFECT_ATTACH_POINT:  Where on the unit the effect attaches
    //* EFFECT_PATH_WATER:    What special effect to attach over water
    //* EFFECT_PATH_GROUND:   What special effect to attach over ground
    //* DEST_RADIUS:          Radius around which destructs die
    //* DEST_RADIUS_SQUARED:  Radius squared around which destructs die
    //* ADJACENT_RADIUS:      Radius for knocking back adjacent units
    //* ADJACENT_FACTOR:      Factor for collision speed transfers
    //* TIMER_INTERVAL:       The interval for the timer that gets run
    //* ISSUE_LAST_ORDER:     A boolean to issue last orders or not
    //*
    private constant string         EFFECT_ATTACH_POINT = "origin"
    private constant string         EFFECT_PATH_WATER   = "MDX\\KnockbackWater.mdx"
    private constant string         EFFECT_PATH_GROUND  = "MDX\\KnockbackDust.mdx"
    private constant real           DEST_RADIUS         = 180.
    private constant real           DEST_RADIUS_SQUARED = DEST_RADIUS*DEST_RADIUS
    private constant real           ADJACENT_RADIUS     = 180.
    private constant real           ADJACENT_FACTOR     = 0.75
    private constant real           TIMER_INTERVAL      = 0.05
    private constant boolean        ISSUE_LAST_ORDER    = true
    
    //*********************************************************
    //* These are static constants used by the system and shouldn't be changed
    //*
    //* Timer:                The timer that runs all of the effects for the spell
    //* Counter:              The counter for how many KB instances exist
    //* HitIndex:             Indexes for a given unit's knockback
    //* Knockers:             The array of all struct instances that exist
    //* Entries:              Counters for specific unit instances in system
    //* ToClear:              How many instances to remove on next run
    //* DesBoolexpr:          The check used for finding destructables
    //* AdjBoolexpr:          The check for picking adjacent units to knockback
    //* DestRect:             The rect used to check for destructables
    //*
    private          timer          Timer               = CreateTimer()
    private          integer        Counter             = 0
    private          integer  array HitIndex
    private          integer  array Knockers
    private          integer  array Entries
    private          integer  array ToClear
    private          boolexpr       DesBoolexpr         = null
    private          boolexpr       AdjBoolexpr         = null
    private          rect           DestRect            = Rect(0,0,1,1)
    
    //* Temporary variables used by the system
    private          real           TempX               = 0.
    private          real           TempY               = 0.
    private          unit           TempUnit1           = null
    private          unit           TempUnit2           = null
endglobals

//* Functions for the destructable destruction
private function KillDests_Check takes nothing returns boolean
    local real x = GetDestructableX(GetFilterDestructable())
    local real y = GetDestructableY(GetFilterDestructable())
    return (TempX-x)*(TempX-x) + (TempY-y)*(TempY-y) <= DEST_RADIUS_SQUARED
endfunction

private function KillDests takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

//* Functions for knocking back adjacent units
private function KnockAdj_Check takes nothing returns boolean
    return TempUnit2 != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempUnit1)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Avul') <= 0
endfunction

//******************************************************************************
//* Some additional functions that can be used
function KnockbackStop takes unit targ returns boolean
    local integer id = GetUnitId(targ)
    set ToClear[id] = Entries[id]
    return ToClear[id] > 0
endfunction
function IsKnockedBack takes unit targ returns boolean
    return Entries[GetUnitId(targ)] > 0
endfunction

//* Struct for the system, I recommend leaving it alone
private struct knocker
    unit Source      = null
    unit Target      = null
    group HitGroup   = null
    effect KBEffect  = null
    integer FXMode   = 0
    boolean KillDest = false
    boolean KnockAdj = false
    boolean ChainAdj = false
    real Decrement   = 0.
    real Displace    = 0.
    real CosA        = 0.
    real SinA        = 0.
    
    public method checkterrain takes knocker n returns integer
        local real x = GetUnitX(n.Target)
        local real y = GetUnitY(n.Target)
        if IsTerrainPathingType(x, y, TERRAIN_PATHING_LAND) then
            return 1
        endif
        if IsTerrainPathingType(x, y, TERRAIN_PATHING_SHALLOW) then
            return 2
        endif
        return 0
    endmethod
    static method create takes unit source, unit targ, real angle, real disp, real dec, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns knocker
        local knocker n = knocker.allocate()
        set n.Target    = targ
        set n.Source    = source
        set n.FXMode    = n.checkterrain(n)
        set n.HitGroup  = NewGroup()
        set n.KillDest  = killDestructables
        set n.KnockAdj  = knockAdjacent
        set n.ChainAdj  = chainAdjacent
        set n.Decrement = dec
        set n.Displace  = disp
        set n.CosA      = Cos(angle)
        set n.SinA      = Sin(angle)
        
        if n.FXMode == 1 then
            set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
        elseif n.FXMode == 2 then
            set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
        debug else
            debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Create): Unknown Terrain Type")
        endif
        
        return n
    endmethod
    private method onDestroy takes nothing returns nothing
        local integer id = GetUnitId(this.Target)
        set Entries[id] = Entries[id] - 1
        if GetWidgetLife(this.Target) > 0.405 and Entries[id] <= 0 and ISSUE_LAST_ORDER then
            call IssueLastOrder(this.Target)
        endif
        call DestroyEffect(this.KBEffect)
        call ReleaseGroup(this.HitGroup)
    endmethod
endstruct

private function Update takes nothing returns nothing
    local unit u       = null
    local unit s       = null
    local rect r       = null
    local knocker n    = 0
    local knocker m    = 0
    local integer i    = Counter - 1
    local integer j    = 0
    local integer mode = 0
    local integer id   = 0
    local real xi      = 0.
    local real yi      = 0.
    local real xf      = 0.
    local real yf      = 0.
    
    loop
        exitwhen i < 0
        set n    = Knockers[i]
        set u    = n.Target
        set mode = n.FXMode
        set id   = GetUnitId(u)
        
        set xi   = GetUnitX(u)
        set yi   = GetUnitY(u)
        
        if n.Displace <= 0 or ToClear[id] > 0 then
            //* Clean up the knockback when it is over
            if ToClear[id] > 0 then
                set ToClear[id] = ToClear[id] - 1
            endif
            call n.destroy()
            set Counter = Counter - 1
            if Counter < 0 then
                call PauseTimer(Timer)
                set Counter = 0
            else
                set Knockers[i] = Knockers[Counter]
            endif
        else
            //* Propagate the knockback in space and time
            set xf = xi + n.Displace*n.CosA
            set yf = yi + n.Displace*n.SinA
            call SetUnitPosition(u, xf, yf)
            set n.FXMode = n.checkterrain(n)
            
            //* Modify the special effect if necessary
            if n.FXMode == 1 and mode == 2 then
                call DestroyEffect(n.KBEffect)
                set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
            elseif n.FXMode == 2 and mode == 1 then
                call DestroyEffect(n.KBEffect)
                set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
            debug elseif n.FXMode == 0 then
                debug call BJDebugMsg(SCOPE_PREFIX+" Error (In Update): Unknown Terrain Type")
            endif
            set n.Displace = n.Displace - n.Decrement
            
            //* Destroy destructables if desired
            if n.KillDest then
                set TempX = GetUnitX(u)
                set TempY = GetUnitY(u)
                call MoveRectTo(DestRect, TempX, TempY)
                call EnumDestructablesInRect(DestRect, DesBoolexpr, function KillDests)
            endif
            
            //* Knockback nearby units if desired
            if n.KnockAdj then
                set xi         = GetUnitX(u)
                set yi         = GetUnitY(u)
                set TempUnit1  = n.Source
                set TempUnit2  = u
                call GroupEnumUnitsInRange(ENUM_GROUP, xi, yi, ADJACENT_RADIUS, AdjBoolexpr)
                loop
                    set s = FirstOfGroup(ENUM_GROUP)
                    exitwhen s == null
                    if not IsUnitInGroup(s, n.HitGroup) then
                        set xf = GetUnitX(s)
                        set yf = GetUnitY(s)
                        call GroupAddUnit(n.HitGroup, s)
                        set m = knocker.create(n.Source, s, Atan2(yf-yi, xf-xi), n.Displace*ADJACENT_FACTOR, n.Decrement, n.KillDest, n.ChainAdj, n.ChainAdj)
                        call GroupAddUnit(m.HitGroup, u)
                        set Knockers[Counter] = m
                        set Counter           = Counter + 1
                    endif
                    call GroupRemoveUnit(ENUM_GROUP, s)
                endloop
            endif
        endif
        set i = i - 1
    endloop
    
    set u = null
    set s = null
endfunction

//******************************************************************************
//* How to knockback a unit
function KnockbackTarget takes unit source, unit targ, real angle, real startspeed, real decrement, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns boolean
    local knocker n  = 0
    local integer id = GetUnitId(targ)
    
    //* Protect users from themselves
    if decrement <= 0. or startspeed <= 0. or targ == null then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Call): Invalid Starting Conditions")
        return false
    endif
    //* Can't chain if you don't knockback adjacent units
    if not knockAdjacent and chainAdjacent then
        set chainAdjacent = false
    endif
    set n = knocker.create(source, targ, angle*bj_DEGTORAD, startspeed*TIMER_INTERVAL, decrement*TIMER_INTERVAL*TIMER_INTERVAL, killDestructables, knockAdjacent, chainAdjacent)
    if Counter == 0 then
        call TimerStart(Timer, TIMER_INTERVAL, true, function Update)
    endif
    
    set Entries[id]       = Entries[id] + 1
    set HitIndex[id]      = Counter + 1
    set Knockers[Counter] = n
    set Counter           = Counter + 1
    return true
endfunction

private function Init takes nothing returns nothing
    call SetRect(DestRect, -DEST_RADIUS, -DEST_RADIUS, DEST_RADIUS, DEST_RADIUS)
    set DesBoolexpr = Condition(function KillDests_Check)
    set AdjBoolexpr = Condition(function KnockAdj_Check)
endfunction
endlibrary

library LastOrder initializer Init needs UnitIndexingUtils
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library has a lot of usefulness for when you want to interface with the
//* last order a unit was given. This can be useful for simulating spell errors
//* and where you'd want to give them back the order they had prior to the spell
//* cast (whereas without this library, they'd just forget their orders).
//* 
//* There are some handy interfacing options for your use here --
//*     function GetLastOrderId takes unit u returns integer
//*     function GetLastOrderString takes unit u returns string
//*     function GetLastOrderType takes unit u returns integer
//*     function GetLastOrderX takes unit u returns real
//*     function GetLastOrderY takes unit u returns real
//*     function GetLastOrderTarget takes unit u returns widget
//*     function AbortOrder takes unit u returns boolean
//*
//* There are also some order commands that can be useful --
//*     function IssueLastOrder takes unit u returns boolean
//*     function IssueSecondLastOrder takes unit u returns boolean
//*     function IsLastOrderFinished takes unit u returns boolean
//* 
//* You can access any information you'd like about the orders for your own
//* order handling needs.
//* 
globals
    //* Storage for last order
    private          integer array Order
    private          integer array Type
    private          widget  array Targ
    private          boolean array Flag
    private          real    array X
    private          real    array Y
    
    //* Storage for second last order
    private          integer array P_Order
    private          integer array P_Type
    private          widget  array P_Targ
    private          boolean array P_Flag
    private          real    array P_X
    private          real    array P_Y
    
    //* Order type variables
            constant integer       ORDER_TYPE_TARGET    = 1
            constant integer       ORDER_TYPE_POINT     = 2
            constant integer       ORDER_TYPE_IMMEDIATE = 3
    
    //* Trigger for the order catching
    private          trigger       OrderTrg             = CreateTrigger()
endglobals

function GetUnitId takes handle h returns integer
    return GetHandleId(h)
endfunction

//**********************************************************
function GetLastOrderId takes unit u returns integer
    return Order[GetUnitId(u)]
endfunction
function GetLastOrderString takes unit u returns string
    return OrderId2String(Order[GetUnitId(u)])
endfunction
function GetLastOrderType takes unit u returns integer
    return Type[GetUnitId(u)]
endfunction
function GetLastOrderX takes unit u returns real
    return X[GetUnitId(u)]
endfunction
function GetLastOrderY takes unit u returns real
    return Y[GetUnitId(u)]
endfunction
function GetLastOrderTarget takes unit u returns widget
    return Targ[GetUnitId(u)]
endfunction
//**********************************************************
private function OrderExclusions takes unit u, integer id returns boolean
    //* Excludes specific orders or unit types from registering with the system
    //* 
    //* 851972: stop
    //*         Stop is excluded from the system, but you can change it by
    //*         adding a check for it below. id == 851972
    //* 
    //* 851971: smart
    //* 851986: move
    //* 851983: attack
    //* 851984: attackground
    //* 851990: patrol
    //* 851993: holdposition
    //*         These are the UI orders that are passed to the system.
    //* 
    //* >= 852055, <= 852762
    //*         These are all spell IDs from defend to incineratearrowoff with
    //*         a bit of leeway at the ends for orders with no strings.
    //* 
    return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction
private function LastOrderFilter takes unit u returns boolean
    //* Some criteria for whether or not a unit's last order should be given
    //* 
    //* INSTANT type orders are excluded because generally, reissuing an instant
    //* order doesn't make sense. You can remove that check below if you'd like,
    //* though.
    //* 
    //* The Type check is really just to ensure that no spell recursion can
    //* occur with IssueLastOrder. The problem with intercepting the spell cast
    //* event is that it happens after the order is 'caught' and registered to
    //* this system. Therefore, to just IssueLastOrder tells it to recast the
    //* spell! That's a problem, so we need a method to eliminate it.
    //* 
    local integer id = GetUnitId(u)
    return u != null and GetWidgetLife(u) > 0.405 and Type[id] != ORDER_TYPE_IMMEDIATE
endfunction
private function SecondLastOrderFilter takes unit u returns boolean
    //* Same as above but with regard to the second last order issued
    local integer id = GetUnitId(u)
    return u != null and GetWidgetLife(u) > 0.405 and P_Type[id] != ORDER_TYPE_IMMEDIATE and P_Order[id] != Order[id]
endfunction
//**********************************************************

function IsLastOrderFinished takes unit u returns boolean
    return (GetUnitCurrentOrder(u) == 0 and Order[GetUnitId(u)] != 851972) or Flag[GetUnitId(u)]
endfunction

function IssueLastOrder takes unit u returns boolean
    local integer id = GetUnitId(u)
    if LastOrderFilter(u) and Order[id] != 0 and not Flag[id] then
        if Type[id] == ORDER_TYPE_TARGET then
            return IssueTargetOrderById(u, Order[id], Targ[id])
        endif
        if Type[id] == ORDER_TYPE_POINT then
            return IssuePointOrderById(u, Order[id], X[id], Y[id])
        endif
        if Type[id] == ORDER_TYPE_IMMEDIATE then
            return IssueImmediateOrderById(u, Order[id])
        endif
    endif
    return false
endfunction

function IssueSecondLastOrder takes unit u returns boolean
    //* This function has to exist because of spell recursion
    local integer id = GetUnitId(u)
    if SecondLastOrderFilter(u) and P_Order[id] != 0 and not P_Flag[id] then
        if P_Type[id] == ORDER_TYPE_TARGET then
            return IssueTargetOrderById(u, P_Order[id], P_Targ[id])
        endif
        if P_Type[id] == ORDER_TYPE_POINT then
            return IssuePointOrderById(u, P_Order[id], P_X[id], P_Y[id])
        endif
        if P_Type[id] == ORDER_TYPE_IMMEDIATE then
            return IssueImmediateOrderById(u, P_Order[id])
        endif
    endif
    return false
endfunction

function AbortOrder takes unit u returns boolean
    if IsUnitPaused(u) then
        return false
    endif
    if not IsUnitPaused(u) then
        call PauseUnit(u, true)
        call IssueImmediateOrder(u, "stop")
        call PauseUnit(u, false)
    endif
    return true
endfunction
//**********************************************************

private function Conditions takes nothing returns boolean
    return OrderExclusions(GetTriggerUnit(), GetIssuedOrderId())
endfunction

private function Actions takes nothing returns nothing
    local unit    u  = GetTriggerUnit()
    local integer id = GetUnitId(u)
    
    //* Store second to last order to eliminate spell recursion
    set P_Order[id]  = Order[id]
    set P_Targ[id]   = Targ[id]
    set P_Type[id]   = Type[id]
    set P_Flag[id]   = Flag[id]
    set P_X[id]      = X[id]
    set P_Y[id]      = Y[id]
    
    set Flag[id]     = false
    set Order[id]    = GetIssuedOrderId()
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
        set Targ[id] = GetOrderTarget()
        set Type[id] = ORDER_TYPE_TARGET
        set X[id]    = GetWidgetX(GetOrderTarget())
        set Y[id]    = GetWidgetY(GetOrderTarget())
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
        set Targ[id] = null
        set Type[id] = ORDER_TYPE_POINT
        set X[id]    = GetOrderPointX()
        set Y[id]    = GetOrderPointY()
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
        set Targ[id] = null
        set Type[id] = ORDER_TYPE_IMMEDIATE
        set X[id]    = GetUnitX(u)
        set Y[id]    = GetUnitY(u)
    debug else
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Order Doesn't Exist")
    endif
    
    set u = null
endfunction
//**********************************************************

private function SpellActions takes nothing returns nothing
    set Flag[GetUnitId(GetTriggerUnit())] = true
endfunction
//**********************************************************

private function Init takes nothing returns nothing
    local trigger trg = CreateTrigger()
    call TriggerAddAction(OrderTrg, function Actions)
    call TriggerAddCondition(OrderTrg, Condition(function Conditions))
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
    
    call TriggerAddAction(trg, function SpellActions)
    call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    
    set trg = null
endfunction
endlibrary

library UnitIndexingUtils initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* -: BLUE FLAVOR :-
//* 
//* This can be used to index units with a unique integer for use with arrays
//* and things like that. This has a limit of 8191 indexes allocated at once in
//* terms of actually being usable in arrays. It won't give you an error if you
//* exceed 8191, but that is an unrealistic limit anyways.
//* 
//* The blue flavor uses a trigger that fires on death of a unit to release the
//* indexes of units. This is useful for maps where ressurection is not an issue
//* and doesn't require an O(n) search inside of a timer callback, making it
//* potentially less taxing on the game.
//* 
//* To use, call GetUnitId on a unit to retrieve its unique integer id. This
//* library allocates a unique index to a unit the instant it is created, which
//* means you can call GetUnitId immediately after creating the unit with no
//* worry.
//* 
//* Function Listing --
//*     function GetUnitId takes unit u returns integer
//* 
private struct unitindex
endstruct

//Function to get the unit's unique integer id, inlines to getting its userdata
private function GetUnitId takes unit u returns integer
    return GetUnitUserData(u)
endfunction

//Filter for units to index 
private function UnitFilter takes nothing returns boolean
    return true
endfunction

//Filter for what units to remove indexes for on death
private function Check takes nothing returns boolean
    return not IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO)
endfunction

private function Clear takes nothing returns nothing
    local unit u = GetTriggerUnit()
    call unitindex(GetUnitId(u)).destroy()
    call SetUnitUserData(u,-1)
    set u = null
endfunction

private function Add takes nothing returns boolean
    call SetUnitUserData(GetFilterUnit(),unitindex.create())
    return true
endfunction

private function GroupAdd takes nothing returns nothing
    call SetUnitUserData(GetEnumUnit(),unitindex.create())
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local region  r = CreateRegion()
    local group   g = CreateGroup()
    local integer i = 0
    
    //Use a filterfunc so units are indexed immediately
    call RegionAddRect(r, bj_mapInitialPlayableArea)
    call TriggerRegisterEnterRegion(t, r, And(Condition(function UnitFilter), Condition(function Add)))
    
    //Loop and group per player to grab all units, including those with locust
    loop
        exitwhen i > 15
        call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function UnitFilter))
        call ForGroup(g, function GroupAdd)
        set i = i + 1
    endloop
    
    //Set up the on-death trigger to clear custom values
    set t = CreateTrigger()
    call TriggerAddAction(t, function Clear)
    call TriggerAddCondition(t, Condition(function Check))
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    
    call DestroyGroup(g)
    set r = null
    set t = null
    set g = null
endfunction
endlibrary

library TerrainPathability initializer Initialization
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This can be used to detect the type of pathing a specific point of terrain
//* is, whether land, shallow water, or deep water. This type of detection
//* should have been easy to do using natives, but the IsTerrainPathable(...)
//* native is very counterintuitive and does not permit easy detection in one
//* call. For that reason, this library was developed.
//*
//* The system requires a dummy unit of some sort. There are no real
//* requirements upon the dummy unit, but it needs a non-zero movement speed.
//* More importantly than the dummy unit, though, it needs a custom windwalk
//* based unit ability with a 0.00 duration and no fade time. Both of those
//* raw id's (for the unit and windwalk dummy) need to be configured below.
//*
//* There is an objectmerger call available for those of you too lazy to build 
//* your own windwalk based ability. Simply uncomment it below and save once,
//* then close and reopen your map and recomment the line.
//*
globals
    constant integer TERRAIN_PATHING_DEEP                           = 1
    constant integer TERRAIN_PATHING_SHALLOW                        = 2
    constant integer TERRAIN_PATHING_LAND                           = 3
    constant integer TERRAIN_PATHING_WALKABLE                       = 4
    
    private unit Dummy                                              = null
    private constant integer DUMMY_UNIT_ID                          = 'hfoo'
    private constant integer DUMMY_WINDWALK_ID                      = 'win&'
    private constant player OWNING_PLAYER                           = Player(15)
    
    //* These variables shouldn't be adjusted
    private real WorldMinX                                          = 0.
    private real WorldMinY                                          = 0.
endglobals

////! external ObjectMerger w3a ANwk win& anam "Collision Ability" ansf "" Owk3 1 0.0 Owk4 1 0 Owk2 1 0.0 Owk1 1 0.0 acdn 1 0.0 ahdu 1 0.0 adur 1 0. aher 0 amcs 1 0

function IsTerrainPathingType takes real x, real y, integer terrainPathingType returns boolean
    local boolean b = false
    if terrainPathingType == TERRAIN_PATHING_DEEP then
        return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
    endif
    if terrainPathingType == TERRAIN_PATHING_SHALLOW then
        return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
    endif
    if terrainPathingType == TERRAIN_PATHING_LAND then
        return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
    endif
    if terrainPathingType == TERRAIN_PATHING_WALKABLE then
        call SetUnitPosition(Dummy, x, y)
        set b = GetUnitX(Dummy) == x and GetUnitY(Dummy) == y and not (not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY))
        call SetUnitX(Dummy, WorldMinX)
        call SetUnitY(Dummy, WorldMinY)
    endif
    return b
endfunction

private function Initialization takes nothing returns nothing
    local rect r = GetWorldBounds()
    
    set WorldMinX = GetRectMinX(r)
    set WorldMinY = GetRectMinY(r)
    set Dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, 0., 0., 0.)
    call UnitAddAbility(Dummy, DUMMY_WINDWALK_ID)
    call UnitAddAbility(Dummy, 'Avul')
    call IssueImmediateOrderById(Dummy, 852129)
    call SetUnitX(Dummy, WorldMinX)
    call SetUnitY(Dummy, WorldMinY)
    
    call RemoveRect(r)
    set r = null
endfunction
endlibrary

library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//*     function NewGroup takes nothing returns group
//*     function ReleaseGroup takes group g returns boolean
//*     function GroupRefresh takes group g returns nothing
//* 
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//* 
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//* 
globals
    //* Group for use with all instant enumerations
    group ENUM_GROUP = CreateGroup()
    
    //* Temporary references for GroupRefresh
    private boolean Flag                                              = false
    private group Refr                                                = null
    
    //* Assorted constants
    private constant integer MAX_HANDLE_COUNT                         = 408000
    private constant integer MIN_HANDLE_ID                            = 0x100000
    
    //* Arrays and counter for the group stack
    private group array Groups
    private integer array Status[MAX_HANDLE_COUNT]
    private integer Count                                             = 0
endglobals

private function H2I takes handle h returns integer
    return GetHandleId(h)
endfunction

private function AddEx takes nothing returns nothing
    if Flag then
        call GroupClear(Refr)
        set Flag = false
    endif
    call GroupAddUnit(Refr, GetEnumUnit())
endfunction
    
function GroupRefresh takes group g returns nothing
    set Flag = true
    set Refr = g
    call ForGroup(Refr, function AddEx)
    if Flag then
        call GroupClear(g)
    endif
endfunction

function NewGroup takes nothing returns group
    if Count == 0 then
        set Groups[0] = CreateGroup()
    else
        set Count = Count - 1
    endif
    set Status[H2I(Groups[Count])-MIN_HANDLE_ID] = 1
    return Groups[Count]
endfunction

function ReleaseGroup takes group g returns boolean
    local integer stat = Status[H2I(g)-MIN_HANDLE_ID]
    if g == null then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Null groups cannot be released")
        return false
    endif
    if stat == 0 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Group not part of stack")
        return false
    endif
    if stat == 2 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Groups cannot be multiply released")
        return false
    endif
    if Count == 8191 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Max groups achieved, destroying group")
        call DestroyGroup(g)
        return false
    endif
    if Count != 8191 then
        call GroupClear(g)
        set Groups[Count]                = g
        set Count                        = Count + 1
        set Status[H2I(g)-MIN_HANDLE_ID] = 2
    endif
    return true
endfunction
endlibrary
 

Sevion

The DIY Ninja
Reaction score
413
I'm too lazy to look through.

It looks like there are lots of differences though. Just use the GroupUtils that comes with the spell.

Chances are that it's more up-to-date compared to my EGUI one. EGUI is old. In fact, EGUI probably should never be used anymore.
 

jig7c

Stop reading me...-statement
Reaction score
123
why is EGUI bad?
can i still use the EGUI systems, in vjass... and not make triggers in GUI?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top