1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. The Results have come out of the old ARENA oven. Check out who won the 30th Texturing Contest!
    Dismiss Notice
  4. Hey guys, we've posted the Results for the 30th Modeling Contest. Check them out!
    Dismiss Notice
  5. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  6. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  7. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Patrol v1.6.w3x
Variables
PatrolSystem
Patrol
Patrol GUI
Patrol GUI Readme Types
Patrol GUI Readme Create
Patrol GUI Readme Dynamic Access
Patrol GUI Readme Destroy
Demo
Demo vJASS
Demo GUI
Cam
Init
Libs
VectorT
Alloc
Table
TimerUtils
Unit Indexer

		
Name Type Is Array Initial Value
PATROL_Create trigger No
PATROL_Destroy trigger No
PATROL_Enabled boolean No
PATROL_GetData trigger No
PATROL_GuardDistance real No
PATROL_Order string No
PATROL_ORDER_ATTACK string No
PATROL_ORDER_MOVE string No
PATROL_PatrolType integer No
PATROL_Point location No
PATROL_RegisterPoint trigger No
PATROL_SetData trigger No
PATROL_TYPE_BACKWARD integer No
PATROL_TYPE_LOOP integer No
PATROL_TYPE_NORMAL integer No
PATROL_Unit unit No
TempPoint location No
TempReal real No
//TESH.scrollpos=615
//TESH.alwaysfold=0
native UnitAlive takes unit u returns boolean
library PatrolPath
    //! runtextmacro DEFINE_STRUCT_VECTOR("", "PatrolPath", "PatrolPoint")
endlibrary
//! zinc
library Patrol  /* v1.5a --hiveworkshop.com/threads/patrol-waypoint-system.276391/

    */
requires VectorT,       /* hiveworkshop.com/threads/containers-vector-t.248942/
    */
         UnitDex,       /* wc3c.net/showthread.php?t=101322
    */
         TimerUtils,    /* hiveworkshop.com/threads/system-unitdex-unit-indexer.248209
    */
         PatrolPath     /*
       
       
    Information
    ¯¯¯¯¯¯¯¯¯¯¯
       
            Patrol allows units to walk certain patrol paths with certain behaviour.
            PatrolPaths can be simple routes, but a unit can also run complex routes.
           
            With "complex" patrols is meant that you can combine multiple patrol
            paths into one bigger, complex patrol path. See demo example.
           
           
    Applied method
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
           
            1. We create a PatrolPath which is a new vector type from VectorT.
           
            2. We create as many PatrolPoint(s) as we need and add them to a PatrolPath.
                (for adding PatrolPoints to a PatrolPath we use the common vector API, see VectorT)
               
            3. We can apply a unit anytime to a certain Path with using our PatrolUnit API.
           
            You are allowed to define a special PatrolType (defines the behaviour) for each patroling unit.
            You can also register your code which runs when ever a PatrolPoint is reached.
           

*/

//! novjass  
 //=================== --------- API --------- ====================

    struct PatrolPoint
           
        real x
        real y
       
        static method create(real x, real y)
       
        method destroy()
       
//  With this you can create and destroy PatrolPoints.
//  You always can change x/y coordinate of a PatrolPoint at any time.

// PatrolPoint is the type that you need to add to a PatrolPath, example:

        local PatrolPath path = PatrolPath.create()
        call path.push(PatrolPoint(0,0))
   
// In the example we create a PatrolPoint(0/0) and add it to our PatrolPath.
// If you wonder where I took the "push" method from, PatrolPath is actually nothing else
// than a vector type from VectorT library. So you can use all API powers from the
// vector library to manage the PatrolPath.
       
===

    struct PatrolType
   
        static constant integer NORMAL     // After patrol finished, the unit walks back to start Point.
        static constant integer BACKWARD   // After patrol finished, the unit walks all the patrol path backwards.
        static constant integer LOOP       // After patrol finished, the unit will instantly be moved to start.

===

    struct PatrolOrder
   
        static constant integer MOVE     // The unit will "move" to points.
        static constant integer ATTACK   // The unit will "attack" to points.

===

    struct PatrolUnit
   
   
        static method create(unit u, PatrolPath pp, PatrolType pt, PatrolOrder po, real guardDistance, boolean reverse) returns thistype
            // Applies a patrolpath with a patroltype to a unit
            // "reverse" means it would go the PatrolPath in reverse, so start at last position (when set to "true")
           
            // guardDistance is only important if the PatrolOrder is ATTACK.
            // it defines the maximum distance before the unit is forced to return patroling.
           
        method destroy()
            // Make the unit no PatrolUnit anymore
           
        method operator unit returns unit
            // the unit which patrols
           
        method operator pathPosition returns integer
        method operator pathPosition= (integer newPos)
            // This is the current position of the unit's PatrolPath.
            // You ONLY need this actually when you register a code (see later).
            // It's advised only to "read" it (see demo), change it only if you know what you're doing.
            // minimum index (start) = 0
            // maximum index (back)  = PatrolPath.size()-1
           
        method operator order returns PatrolOrder
        method operator order= (PatrolOrder p)
       
        method operator guardDistance returns real
        method operator guardDistance= (real r)
   
        method operator enabled  returns boolean
        method operator enabled= (boolean flag)
            // enables/disable an instance
           
        method operator reverse  returns boolean
        method operator reverse= (boolean flag)
            // "true" means the unit goes the PatrolPath in reverse way.
       
        method operator path  returns PatrolPath
        method operator path= (PatrolPath p)
            // you can apply a new path at any time
            // when newly applied, it will start at StartPosition,
            // or at respectivly at LastPosition, if "reverse" is "true".
       
        method operator patrolType  returns PatrolType
        method operator patrolType= (PatrolType p)
            // you can change patrol type at any time
           
        static method operator enabledAll= (boolean flag)
            // enable or disable all instances
           
        static method operator[] (unit u) returns thistype
            // for example:
                set PatrolUnit[unit].reverse = true
               
        static method register(boolexpr be) returns triggercondition
        static method unregister(triggercondition tc)
            // You can register and unregister boolexrepssions with these functions.
            // Registered code will run when ever a PatrolUnit reaches a PatrolPoint.
           
            // When the registered code returns "true", the system will normaly
            //     contitunue the unit's Patrol behaviour.
            // When the registered code returns "false", the system thinks that
            //     you made custom changes and will skip the current PatrolPoint.
           
        method registerPatrolUnit(boolexpr be) returns triggercondition
        method unregisterPatrolUnit(triggercondition tc)
            // same as above, but specified to your instance
           
// !! Just as info, first the globaly registered code runs, and then the instance-specified one.
           
        static method operator currentPatrolUnit returns thistype
            // use this operator to access the current instance inside registered code.
           
            // e.g: PatrolUnit currentInstance = PatrolUnit.currentPatrolUnit;
       
           
===

    function PatrolPathHardDestroy(PatrolPath p)
        // Of course you always can use the standard destructor .destroy()
        // The major difference to this, is that this "Hard Destroy" will also destroy
        // all to the PatrolPath binded PatrolPoints. With standard .destroy() you solely  
        // destroy the PatrolPath itself, but PatrolPoints would still exist.  
        // The reason for that is, because it is technicaly allowed to use one PatrolPoint
        // for multiple PatrolPaths.
        // You only should use this function if you truely don't need the binded points anymore.
       
       
// Look at demo for examples.
       
//! endnovjass    
{
//=================== --------- Config --------- ====================  
 
    private constant boolean DISABLE_ON_DEATH    = true;    // When a PatrolUnit dies, the instance will be automaticaly disabled
                                                            // You will need to enable it by your own again.
    private constant boolean DESTROY_ON_DEATH    = false;   // When a PatrolUnit dies, the instance will be automaticaly destroyed.
   
    private constant boolean FORMATION_ENABLED   = false;   // Set this to true in case players who control patrol units (can) have the
                                                            // formation enabled in their gameplay options.
                                                            // If you have formation enabled, you can bug patrol units with it,
                                                            // and they would start going crazy when you try to keep some units in format
                                                            // that try to reach the same patrol point. So when you set this to "true", the system
                                                            // will try to fix this with giving patrol orders to a point with random/minimal offset of
                                                            // the wanted x/ym so 2 units will likely never have the problem in being in formated movement
                                                            // with also trying to patrol to the very same x/y at the same time.
                                                           
                                                            // but it is just recommended that you leave the formation disabled and set it here to "false".
   
    private constant real    MAX_RANGE           = 20;      // The tolerance distance a unit may have to a patrol point.
   
                                           
//=================== -------------------------- ====================
   
    public struct PatrolPoint{
        real x;
        real y;
        private boolean p_exists;   // private_exists
        static method create(real x, real y) -> thistype{
            thistype this = allocate();
            this.x        = x;
            this.y        = y;
            this.p_exists = true;
            return this;
        }
        method destroy(){
            if (p_exists){
                deallocate();
                p_exists = false;
            }
        }
       
        // user doesn't need to know this exists,
        // but it makes no harm
        method operator exists() -> boolean{
            return p_exists;
        }
    }
   
    public struct PatrolType extends array{
        static constant integer NORMAL   = 1;   // After finished, the unit walks back to start Point.
        static constant integer BACKWARD = 2;   // After finished, the unit walks all the patrol path backwards.
        static constant integer LOOP     = 3;   // After finished, the unit will instantly be moved to start.
       
        static constant integer MAXIMUM_AMOUNT = 3;
    }
   
    public struct PatrolOrder extends array{
        static constant integer MOVE   = 851986;  
        static constant integer ATTACK = 851983;  
    }
   
    private constant real INTERVAL  = 0.031250000;
   
    public struct PatrolUnit extends array{
       
        private static group Group  = CreateGroup();
        private static group Backup = CreateGroup();
        private static group Temp;
       
        private static trigger  handler = CreateTrigger();  // global handler
        private static thistype p_patrolUnit;               // access in registered code
       
//      members
       
        private unit    p_u;               // private_unit
        private timer   clock;
        private integer incrementer;       // Defines if current position is going forward or backward in PatrolPath
        private integer p_pathPosition;    // Current position in PatrolPath vector
        private trigger p_handler;         // instance specific handler
        private boolean p_reverse;         // private_reverse
        private boolean p_enabled;         // private_enabled
       
        private real p_guardDistance;
        private trigger guardHandler;
       
        private PatrolType p_patrolType;
        private PatrolOrder p_order;
        private PatrolPath p_path;
       
        // x/y of the point the unit needs to return, in case it is chasing/fighting some unit
        private real xReturn;
        private real yReturn;
        private boolean isChasing;
       
        method destroy(){
            ReleaseTimer(clock);
           
            if(p_handler != null){
                DestroyTrigger(p_handler);
                p_handler = null;
            }
           
            if(guardHandler != null){
                DestroyTrigger(guardHandler);
                guardHandler = null;
            }
           
            p_enabled = false;
            GroupRemoveUnit(Group, p_u);
            p_u = null;
            clock = null;
        }
       
        private static method callback(){
            thistype this = GetTimerData(GetExpiredTimer());
            real distance, x, y;
           
            static if(DESTROY_ON_DEATH){
                if (!UnitAlive(p_u)){
                    destroy();
                    return;
                }
            }
            static if(DISABLE_ON_DEATH){
                if (!UnitAlive(p_u)){
                    PauseTimer(clock);
                    p_enabled = false;
                    return;
                }
            }
           
            if(p_path.empty()){
                debug BJDebugMsg(thistype.callback.name + ": instance " + I2S(this) + " has an invalid or empty PatrolPath.");
                return;
            }
            else if(!p_path[p_pathPosition].exists){
                debug BJDebugMsg(thistype.callback.name + ": instance " + I2S(this) + " tries to acces a removed/invalid PatrolPoint.");
                return;
            }
           
            x = GetUnitX(p_u); y = GetUnitY(p_u);
            distance = SquareRoot((p_path[p_pathPosition].x-x)*(p_path[p_pathPosition].x-x) + (p_path[p_pathPosition].y-y)*(p_path[p_pathPosition].y-y));
           
            if (distance <= MAX_RANGE) {
               
                p_patrolUnit = this;
                TriggerEvaluate(handler);
                if(p_handler != null)
                    TriggerEvaluate(p_handler);

                if (p_patrolType == PatrolType.NORMAL){
                    if(p_path[p_pathPosition] == p_path.back() && !reverse){
                        p_pathPosition = 0;
                    }
                    else if(reverse && p_path[p_pathPosition] == p_path.front()){
                        p_pathPosition = p_path.size()-1;
                    }
                    else {
                        p_pathPosition = p_pathPosition + incrementer;
                    }
                }
                else if(p_patrolType == PatrolType.BACKWARD){
                    if(p_path[p_pathPosition] == p_path.back()){
                        incrementer = -1;
                    }
                    else if (p_path[p_pathPosition] == p_path.front()){
                        incrementer = 1;
                    }
                    p_pathPosition = p_pathPosition + incrementer;
                }
                else if(p_patrolType == PatrolType.LOOP){
                    if(p_path[p_pathPosition] == p_path.back() && !reverse){
                        SetUnitX(p_u, p_path.front().x);
                        SetUnitY(p_u, p_path.front().y);
                        p_pathPosition = 1;
                       
                    }
                    else if(reverse && p_path[p_pathPosition] == p_path.front()){
                        SetUnitX(p_u, p_path.back().x);
                        SetUnitY(p_u, p_path.back().y);
                        p_pathPosition = p_path.size()-2;
                    }
                    else {
                        p_pathPosition = p_pathPosition + incrementer;
                    }
                }
               
                if (FORMATION_ENABLED) {
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x + GetRandomReal(0, 0.1), p_path[p_pathPosition].y + GetRandomReal(0, 0.1));
                }
                else{
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x , p_path[p_pathPosition].y);
                }
            }
           
            // when the unit got off the path for some reason (maybe after a fight with enemies),
            // and is being detected without a current order, we re-order it to continue patroling.
            if (GetUnitCurrentOrder(p_u) == null){
           
               if (FORMATION_ENABLED) {
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x + GetRandomReal(0, 0.1), p_path[p_pathPosition].y + GetRandomReal(0, 0.1));
                }
                else{
                    IssuePointOrderById(p_u, p_order, p_path[p_pathPosition].x , p_path[p_pathPosition].y);
                }
               
            }
        }
       
       
        // callback for when a unit is chasing an other unit
        private static method callback_2(){
            thistype this = GetTimerData(GetExpiredTimer());
            real distance, x, y;
            static if(DESTROY_ON_DEATH){
                if (!UnitAlive(p_u)){
                    destroy();
                    return;
                }
            }
            static if(DISABLE_ON_DEATH){
                if (!UnitAlive(p_u)){
                    PauseTimer(clock);
                    p_enabled = false;
                    return;
                }
            }
           
            x = GetUnitX(p_u); y = GetUnitY(p_u);
            distance = SquareRoot((xReturn-x)*(xReturn-x) + (yReturn-y)*(yReturn-y));
           
            // unit returned to path, it can continue patroling
            if(!isChasing && distance <= MAX_RANGE){
                TimerStart(clock, INTERVAL, true, function thistype.callback);
            }
           
            // unit is out of max range, it gets ordered moving back
            if(distance > p_guardDistance){
                isChasing = false;
                IssuePointOrderById(p_u, PatrolOrder.MOVE, xReturn, yReturn);
            }
           
            // unit is not chasing anymore, so return
            if (GetUnitCurrentOrder(p_u) == null){
                isChasing = false;
                IssuePointOrderById(p_u, PatrolOrder.MOVE, xReturn, yReturn);
            }
        }
       
        private static method onTargetAquired() -> boolean{
            thistype this = GetUnitId(GetTriggerUnit());
            if (!isChasing){
                xReturn = GetUnitX(p_u);
                yReturn = GetUnitY(p_u);
            }
            isChasing = true;
            TimerStart(clock, INTERVAL, true, function thistype.callback_2);
            return false;
        }
       
        static method create(unit u, PatrolPath pp, PatrolType pt, PatrolOrder po, real gd, boolean r) -> thistype{
            thistype this;
           
            if(integer(pt) < 1 || integer(pt) > 3){
                debug BJDebugMsg(thistype.create.name + ": unit with id " + I2S(GetHandleId(u)) + " tries to apply an invalid PatrolType.");
                return 0;
            }
            if(!(po == PatrolOrder.MOVE || po == PatrolOrder.ATTACK)){
                debug BJDebugMsg(thistype.create.name + ": unit with id" + I2S(GetHandleId(u)) + " tries to apply an invalid PatrolOrder.");
                return 0;
            }
            if(IsUnitInGroup(u, Group)){
                debug BJDebugMsg(thistype.create.name + ": unit with id" + I2S(GetHandleId(u)) + " already is a PatrolUnit.");
                return 0;
            }
           
            this            = GetUnitId(u);
            clock           = NewTimerEx(this);
            p_u             = u;
            p_order         = po;
            p_path          = pp;
            p_patrolType    = pt;
            p_reverse       = r;
            p_enabled       = true;
            p_handler       = null;
            isChasing       = false;
            p_guardDistance = gd;
            GroupAddUnit(Group, p_u);
           
            if(po == PatrolOrder.ATTACK){
                guardHandler = CreateTrigger();
                TriggerAddCondition(guardHandler, Condition(function thistype.onTargetAquired));
                TriggerRegisterUnitEvent(guardHandler, u, EVENT_UNIT_ACQUIRED_TARGET);
            }
           
            if(r){
                p_pathPosition = p_path.size()-1;
                incrementer    = -1;
            }
            else{
                p_pathPosition = 0;
                incrementer    = 1;
            }
            TimerStart(clock, INTERVAL, true, function thistype.callback);
            return this;
        }
       
       
        method operator patrolType() -> PatrolType{
            return p_patrolType;
        }
        method operator patrolType=(PatrolType p){
            if(integer(p) > 0 && integer(p) <= PatrolType.MAXIMUM_AMOUNT){
                p_patrolType = p;
            }
            else{
                debug BJDebugMsg("Patrol: operator patrolType=: " + ": instance " + I2S(this) + " tries to apply an invalid PatrolType.");
            }
        }
       
        method operator reverse() -> boolean{
            return p_reverse;
        }
        method operator reverse=(boolean flag){
            if(flag != p_reverse){
                incrementer   = -incrementer;
                p_reverse = flag;
            }
        }
       
        method operator path() -> PatrolPath{
            return p_path;
        }
        method operator path=(PatrolPath p){
            p_path = p;
            if(reverse){
                p_pathPosition = p_path.size()-1;
                incrementer    = -1;
            }
            else{
                p_pathPosition = 0;
                incrementer    = 1;
            }
        }
       
        // this exists so the user has no chance to "null" the
        // unit variable in public access.
        method operator unit()-> unit{
            return p_u;
        }
       
        method operator pathPosition()-> integer{
            return p_pathPosition;
        }
        method operator pathPosition=(integer i){
            if (0 <= i && i <= (p_path.size()-1))
                p_pathPosition = i;
            else
                debug BJDebugMsg("Patrol: operator pathPosition=: " + ": instance " + I2S(this) + " tries to apply an invalid pathPosition.");
        }
       
        method operator guardDistance() -> real{
            return p_guardDistance;
        }
        method operator guardDistance=(real r){
            p_guardDistance = r;
        }
       
        method operator order()-> PatrolOrder{
            return p_order;
        }
        method operator order=(PatrolOrder p){
            if (p == PatrolOrder.MOVE || p == PatrolOrder.ATTACK){
                if(p == PatrolOrder.ATTACK && p_order != p){
                    guardHandler = CreateTrigger();
                    TriggerAddCondition(guardHandler, Condition(function thistype.onTargetAquired));
                    TriggerRegisterUnitEvent(guardHandler, p_u, EVENT_UNIT_ACQUIRED_TARGET);
                }
                else if(p_order == PatrolOrder.ATTACK && p == PatrolOrder.MOVE){
                    DestroyTrigger(p_handler);
                    p_handler = null;
                }
                p_order = p;
            }
            else{
                debug BJDebugMsg("Patrol: operator order=: " + ": instance " + I2S(this) + " tries to apply an invalid PatrolOrder.");
            }
        }
       
        method registerPatrolUnit(boolexpr be) -> triggercondition{
            if(p_handler == null){
                p_handler = CreateTrigger();
            }
            return TriggerAddCondition(p_handler, be);
        }
        method unregisterPatrolUnit(triggercondition tc){
            TriggerRemoveCondition(p_handler, tc);
        }
       
        method operator enabled() -> boolean{
            return p_enabled;
        }
        method operator enabled=(boolean flag){
            if(flag && !(p_enabled)){
                TimerStart(clock, INTERVAL, true, function thistype.callback);
                if(p_order == PatrolOrder.ATTACK){
                    EnableTrigger(guardHandler);
                }
            }
            else if(!(flag) && p_enabled){
                PauseTimer(clock);
                if(p_order == PatrolOrder.ATTACK){
                    DisableTrigger(guardHandler);
                }
            }
            p_enabled = flag;
        }
       
        static method operator enabledAll=(boolean flag){
            unit fog;
            fog = FirstOfGroup(Group);
            while(fog != null){
                thistype(GetUnitId(fog)).enabled = flag;
                GroupAddUnit(Backup,fog);
                GroupRemoveUnit(Group,fog);
                fog = FirstOfGroup(Group);
            }
            Temp = Group;
            Group = Backup;
            Backup = Temp;
        }
       
        static method operator[](unit u)-> thistype{
            if(!IsUnitInGroup(u, Group)){
                debug BJDebugMsg("Patrol: operator []: " + ": unit with id " + I2S(GetHandleId(u)) + " is not a PatrolUnit yet.");
                return 0;
            }
            return GetUnitId(u);
        }
       
        static method register(boolexpr be) -> triggercondition{
            return TriggerAddCondition(thistype.handler, be);
        }
        static method unregister(triggercondition tc){
            TriggerRemoveCondition(thistype.handler, tc);
        }
       
        static method operator currentPatrolUnit()->thistype{
            return p_patrolUnit;
        }
       
        // wow, static if's aren't allowed globaly in Zinc :(
        private static method onDeindex()->boolean{
            if (IsUnitInGroup(GetIndexedUnit(), Group)) {
                thistype(GetIndexedUnitId()).destroy();
            }
            return false;
        }
       
        // wow, static if's still aren't allowed globaly in Zinc :(
        private static method onInit(){
            OnUnitDeindex(function thistype.onDeindex);
        }
   
    } // end of struct
   
    public function PatrolPathHardDestroy (PatrolPath p){
        integer i = p.size() - 1;
        while(i >= 0){
            p[i].destroy();
            i = i - 1;
        }
        p.destroy();
    }
}
//! endzinc
 
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! zinc
library PatrolGUI requires Patrol /* v1.1 -- hiveworkshop.com/threads/patrol-waypoint-system.276391/ */{

    PatrolPath tempPath = 0;
    Table table;
   
    function onRegister(){
        if(GetHandleId(udg_PATROL_Point) == 0){
            debug BJDebugMsg("ERROR: PatrolGUI - Register - Point is invalid or does not exist.");
            return;
        }
        if (tempPath == 0)
            tempPath = PatrolPath.create();
        tempPath.push(PatrolPoint.create(GetLocationX(udg_PATROL_Point), GetLocationY(udg_PATROL_Point)));
    }

    function onCreate(){
        if(udg_PATROL_Order != udg_PATROL_ORDER_MOVE && udg_PATROL_Order != udg_PATROL_ORDER_ATTACK){
            debug BJDebugMsg("ERROR: PatrolGUI - Create - Invalid PatrolOrder");
            return;
        }
        if(udg_PATROL_PatrolType < 1 || udg_PATROL_PatrolType > 3){
            debug BJDebugMsg("ERROR: PatrolGUI - Create - Invalid PatrolType.");
            return;
        }
        if(tempPath == 0){
            debug BJDebugMsg("ERROR: PatrolGUI - Create - No Points are registered.");
            return;
        }  
       
        PatrolUnit.create(udg_PATROL_Unit, tempPath, udg_PATROL_PatrolType, OrderId(udg_PATROL_Order), udg_PATROL_GuardDistance, false).enabled = udg_PATROL_Enabled;
        table.integer[GetHandleId(udg_PATROL_Unit)] = tempPath;
        tempPath = 0;
    }
   
    function onDestroy(){
        integer id = GetHandleId(udg_PATROL_Unit);
        if(table.integer.has(id)){
            PatrolPathHardDestroy(PatrolPath(table.integer[id]));
            table.integer.remove(id);
        }
        else
            debug BJDebugMsg("ERROR: PatrolGUI - Destroy - Unit is no PatrolUnit.");
    }
   
    function onGet(){
        PatrolUnit this = PatrolUnit[udg_PATROL_Unit];
        udg_PATROL_Enabled       = this.enabled;
        udg_PATROL_PatrolType    = this.patrolType;
        udg_PATROL_Order         = OrderId2String(this.order);
        udg_PATROL_GuardDistance = this.guardDistance;
    }
   
    function onSet(){
        PatrolUnit this;
       
        if(udg_PATROL_Order != udg_PATROL_ORDER_MOVE && udg_PATROL_Order != udg_PATROL_ORDER_ATTACK){
            debug BJDebugMsg("ERROR: PatrolGUI - SetData - Invalid PatrolOrder");
            return;
        }
        if(udg_PATROL_PatrolType < 1 || udg_PATROL_PatrolType > 3){
            debug BJDebugMsg("ERROR: PatrolGUI - SetData - Invalid PatrolType.");
            return;
        }
   
        this = PatrolUnit[udg_PATROL_Unit];
        this.enabled       = udg_PATROL_Enabled;
        this.patrolType    = udg_PATROL_PatrolType;
        this.order         = OrderId(udg_PATROL_Order);
        this.guardDistance = udg_PATROL_GuardDistance;
    }
   
    function init(){
        udg_PATROL_TYPE_NORMAL   = PatrolType.NORMAL;
        udg_PATROL_TYPE_BACKWARD = PatrolType.BACKWARD;
        udg_PATROL_TYPE_LOOP     = PatrolType.LOOP;
       
        udg_PATROL_ORDER_MOVE   = "move";
        udg_PATROL_ORDER_ATTACK = "attack";
       
        DestroyTimer(GetExpiredTimer());
    }
   
    function onInit(){
        table = Table.create();
        TimerStart(CreateTimer(), 0, false, function init);
        udg_PATROL_Create        = CreateTrigger();
        udg_PATROL_Destroy       = CreateTrigger();
        udg_PATROL_RegisterPoint = CreateTrigger();
        udg_PATROL_GetData       = CreateTrigger();
        udg_PATROL_SetData       = CreateTrigger();
       
        TriggerAddAction(udg_PATROL_Create, function onCreate);
        TriggerAddAction(udg_PATROL_RegisterPoint, function onRegister);
        TriggerAddAction(udg_PATROL_Destroy, function onDestroy);
        TriggerAddAction(udg_PATROL_GetData, function onGet);
        TriggerAddAction(udg_PATROL_SetData, function onSet);
    }
}
//! endzinc
Patrol GUI Readme Types
  Events
  Conditions
  Actions
    -------- PATROL_TYPE_NORMAL --------
    -------- ---------------------------------- --------
    -------- After finished, the unit walks back to start Point. --------
    -------- ---------------------------------- --------
    -------- PATROL_TYPE_BACKWARD --------
    -------- ---------------------------------- --------
    -------- After finished, the unit walks all the patrol path backwards. --------
    -------- ---------------------------------- --------
    -------- PATROL_TYPE_LOOP --------
    -------- ---------------------------------- --------
    -------- After finished, the unit will instantly be moved to start. --------
    -------- ---------------------------------- --------
    -------- ---------------------------------- --------
    -------- PATROL_TYPE_MOVE --------
    -------- ---------------------------------- --------
    -------- The unit will "move" to points. --------
    -------- ---------------------------------- --------
    -------- PATROL_TYPE_ATTACK --------
    -------- ---------------------------------- --------
    -------- The unit will "attack" to points. --------
Patrol GUI Readme Create
  Events
  Conditions
  Actions
    -------- ---------------------------------- --------
    -------- How to create a PatrolUnit --------
    -------- ---------------------------------- --------
    -------- ---------------------------------- --------
    -------- Define the unit --------
    -------- ---------------------------------- --------
    Set PATROL_Unit = No unit
    -------- ---------------------------------- --------
    -------- Define a Point and register it to the system --------
    -------- register as many points as you want --------
    -------- ---------------------------------- --------
    Set PATROL_Point = (Point(0, 0))
    Trigger - Run PATROL_RegisterPoint (ignoring conditions)
    Custom script: call RemoveLocation(udg_PATROL_Point)
    -------- ---------------------------------- --------
    Set PATROL_Point = (Point(100.00, 100.00))
    Trigger - Run PATROL_RegisterPoint (ignoring conditions)
    Custom script: call RemoveLocation(udg_PATROL_Point)
    -------- ---------------------------------- --------
    -------- Define a patrol type, and a order --------
    -------- ---------------------------------- --------
    Set PATROL_PatrolType = PATROL_TYPE_NORMAL
    Set PATROL_Order = PATROL_ORDER_MOVE
    -------- ---------------------------------- --------
    Set PATROL_Enabled = True
    -------- ---------------------------------- --------
    -------- GuadDistance is the maximum range the unit can have until it returns to patroling, in case it's chasing a unit --------
    -------- GuardDistance is only important for PATROL_ORDER_ATTACK --------
    Set PATROL_GuardDistance = 0.00
    -------- ---------------------------------- --------
    -------- To finish run Create trigger --------
    Trigger - Run PATROL_Create (ignoring conditions)
Patrol GUI Readme Dynamic Access
  Events
  Conditions
  Actions
    -------- ---------------------------------- --------
    -------- How to access data at any time --------
    -------- ---------------------------------- --------
    -------- Define our unit --------
    Set PATROL_Unit = No unit
    -------- ---------------------------------- --------
    -------- Run GetData (always!) --------
    Trigger - Run PATROL_GetData (ignoring conditions)
    -------- ---------------------------------- --------
    -------- now you can read following data: --------
    Set PATROL_PatrolType = PATROL_TYPE_NORMAL
    Set PATROL_Order = PATROL_ORDER_MOVE
    Set PATROL_Enabled = True
    Set PATROL_GuardDistance = 0.00
    -------- ---------------------------------- --------
    -------- ---------------------------------- --------
    -------- AFTER you run GetData, you can change some data if you want, for example: --------
    -------- ---------------------------------- --------
    -------- we wanna disable the PatrolUnit --------
    Set PATROL_Enabled = False
    -------- ---------------------------------- --------
    Trigger - Run PATROL_SetData (ignoring conditions)
Patrol GUI Readme Destroy
  Events
  Conditions
  Actions
    -------- ---------------------------------- --------
    -------- How to destroy a PatrolUnit --------
    -------- ---------------------------------- --------
    -------- ---------------------------------- --------
    -------- Define the unit --------
    -------- Run the destroy trigger --------
    -------- ---------------------------------- --------
    Set PATROL_Unit = No unit
    Trigger - Run PATROL_Destroy (ignoring conditions)
//TESH.scrollpos=30
//TESH.alwaysfold=0
//! zinc
library PatrolDemo requires Patrol{

    PatrolPath Route1;
    PatrolPath Route2;
    PatrolPath Route3;
   
    // these 2 will form our complex patrol
    PatrolPath Route4;
    PatrolPath Route5;
   
   
    // Sqaure route
    function InitRoute1(){
        // First we create a new PatrolPath
        Route1 = PatrolPath.create();
       
        // Now we create and directly add some PatrolPoints
        // We use the vector function "push"
        Route1.push(PatrolPoint.create(0, 0));
        Route1.push(PatrolPoint.create(500, 0));
        Route1.push(PatrolPoint.create(500, 500));
        Route1.push(PatrolPoint.create(0, 500));
    }
   
   
    // Triangle route
    function InitRoute2(){
        Route2 = PatrolPath.create();
       
        // We also can use the vector API like this, in one line
        Route2.push(PatrolPoint.create(-500, 0)).push(PatrolPoint.create(-500, 500)).push(PatrolPoint.create(0, 500));
    }
   
    // Line route
    function InitRoute3(){
        Route3 = PatrolPath.create();
        Route3.push(PatrolPoint.create(0, -700)).push(PatrolPoint.create(0, 0));
    }
   
    // Line route
    function InitRoute4(){
        Route4 = PatrolPath.create();
        Route4.push(PatrolPoint.create(-400, -100)).push(PatrolPoint.create(400, -100));
    }
   
    // Line route
    function InitRoute5(){
        Route5 = PatrolPath.create();
        Route5 = Route5.push(PatrolPoint.create(-400, -600)).push(PatrolPoint.create(400, -600));
    }
   
    // Pause on Esc
    boolean enabled = true;
    function onEsc(){
        enabled = !enabled;
        PatrolUnit.enabledAll = enabled;
    }
   
    // Will be our complex patrol (see also later)
    function complexPatrolEvent()-> boolean{
       
        // instance who triggered the event:
        PatrolUnit this = PatrolUnit.currentPatrolUnit;
       
       
        // when the current path position is the end of Route4,
        // we move the unit to start of Route5 and define the new Route
       
        if(this.path[this.pathPosition] == Route4.back()){
            this.path = Route5;
            SetUnitX(this.unit, Route5.front().x);
            SetUnitY(this.unit, Route5.front().y);
        }
       
        // when the current path position is the end of Route5,
        // we move the unit to start of Route4 and define the new Route
       
        if(this.path[this.pathPosition] == Route5.back()){
            this.path = Route4;
            SetUnitX(this.unit, Route4.front().x);
            SetUnitY(this.unit, Route4.front().y);
        }
       
        // !! You need to learn the VectorT API, like back() and front() to properly work with this.
       
        return false;
    }

    function onInit(){
        trigger t = CreateTrigger();
        player p = GetLocalPlayer();
        unit u;
       
        InitRoute1();
        InitRoute2();
        InitRoute3();
        InitRoute4();
        InitRoute5();
       
//      To each route we apply 2 units.
//      One of them will run normaly, and the other one reverse.
         
//      For us to remind how the create function looks:
//! novjass
        static method create(unit u, PatrolPath pp, PatrolType pt, PatrolOrder po, real guardDistance, boolean reverse) returns thistype
//! endnovjass
       
        // footman is for normal patrol
        u = CreateUnit(p, 'hfoo', 0, 0, 0);
        UnitAddAbility(u, 'Aeth');   //ghost to disable collision
        PatrolUnit.create(u, Route1, PatrolType.NORMAL, PatrolOrder.MOVE, 0, false);
        u = CreateUnit(p, 'hfoo', 0, 500, 0);
        UnitAddAbility(u, 'Aeth');   //ghost to disable collision
        PatrolUnit.create(u, Route1, PatrolType.NORMAL, PatrolOrder.MOVE, 0, true);
       
        // gyrphon is for backward patrol
        u = CreateUnit(p, 'hgry', -500, 0, 0);
        UnitAddAbility(u, 'Aeth');
        PatrolUnit.create(u, Route2, PatrolType.BACKWARD, PatrolOrder.MOVE, 0, false);
        u = CreateUnit(p, 'hgry', 0, 500, 0);
        UnitAddAbility(u, 'Aeth');
        PatrolUnit.create(u, Route2, PatrolType.BACKWARD, PatrolOrder.MOVE, 0, true);
       
        // knight is for loop patrol
        u = CreateUnit(p, 'hkni', 0, -700, 0);
        UnitAddAbility(u, 'Aeth');
        PatrolUnit.create(u, Route3, PatrolType.LOOP, PatrolOrder.MOVE, 0, false);
        u = CreateUnit(p, 'hkni', 0, 0, 0);
        UnitAddAbility(u, 'Aeth');
        PatrolUnit.create(u, Route3, PatrolType.LOOP, PatrolOrder.MOVE, 0, true);
       
       
        // For our last example, a complex patrol,
        // we create only one unit, which runs not reverse.
        // Technicaly we could also run complex reverse patrols,
        // but this would require us to write more logics inside
        // the "function specialPatrolEvent", and we don't want this.:)
       
        // p.s. I call it "complex patrol", because it is a combination of mutiple PatrolPaths
       
        // spell breaker is for complex patrol
        u = CreateUnit(p, 'hspt', -400, -100, 0);
        UnitAddAbility(u, 'Aeth');
        PatrolUnit.create(u, Route4, PatrolType.NORMAL, PatrolOrder.MOVE, 0, false);
        PatrolUnit[u].registerPatrolUnit(Filter(function complexPatrolEvent)); // fires only for this instance :)
       
        TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_END_CINEMATIC);
        TriggerAddAction(t, function onEsc);
        BJDebugMsg("Press 'Esc' button to pause/unpause all patrol units.");
    }
}
//! endzinc
Demo GUI
  Events
    Time - Elapsed game time is 0.01 seconds
  Conditions
  Actions
    Set TempPoint = (Point(600.00, 0.00))
    Unit - Create 1 Grunt for Player 1 (Red) at TempPoint facing Default building facing degrees
    Set PATROL_Unit = (Last created unit)
    -------- for no collision --------
    Unit - Add Ghost (Visible) to PATROL_Unit
    Custom script: call RemoveLocation(udg_TempPoint)
    -------- ---------------------------------- --------
    -------- ( here we make a circular patrol) --------
    -------- ---------------------------------- --------
    Set TempReal = 0.00
    Set TempPoint = (Point(0, 0))
    For each (Integer A) from 0 to 31, do (Actions)
      Loop - Actions
        Set TempReal = ((Real((Integer A))) x (360.00 / 32.00))
        -------- ---------------------------------- --------
        Set PATROL_Point = (TempPoint offset by 600.00 towards TempReal degrees)
        Trigger - Run PATROL_RegisterPoint (ignoring conditions)
        Custom script: call RemoveLocation(udg_PATROL_Point)
    Custom script: call RemoveLocation(udg_TempPoint)
    -------- ---------------------------------- --------
    Set PATROL_PatrolType = PATROL_TYPE_NORMAL
    Set PATROL_Order = PATROL_ORDER_ATTACK
    Set PATROL_GuardDistance = 1000.00
    Set PATROL_Enabled = True
    Trigger - Run PATROL_Create (ignoring conditions)
Cam
  Events
    Map initialization
    Time - Every 2 seconds of game time
  Conditions
  Actions
    Camera - Set Player 1 (Red)'s camera Distance to target to 2700.00 over 0 seconds
Init
  Events
    Map initialization
  Conditions
  Actions
    Visibility - Disable fog of war
    Visibility - Disable black mask
//TESH.scrollpos=80
//TESH.alwaysfold=0
/*****************************************************************************
*
*    Vector<T> v1.1.6.6
*       by Bannar aka Spinnaker
*
*    Dynamic contiguous array.
*
******************************************************************************
*
*    Requirements:
*
*       Table by Bribe
*          hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
*       Alloc - choose whatever you like
*
******************************************************************************
*
*    Implementation:
*
*       macro DEFINE_STRUCT_VECTOR takes ACCESS, NAME, TYPE
*
*       macro DEFINE_VECTOR takes ACCESS, NAME, TYPE
*
*          ACCESS - encapsulation, choose retriction access
*            NAME - name of vector type
*            TYPE - type of values stored
*
******************************************************************************
*
*    struct API:
*
*       General:
*
*        | static method create takes nothing returns thistype
*        |    default ctor
*        |
*        | static method operator [] takes thistype vec returns thistype
*        |    copy ctor
*        |
*        | method destroy takes nothing returns nothing
*        |    default dctor
*        |
*        | method empty takes nothing returns boolean
*        |    checks whether the vector is empty
*        |
*        | method size takes nothing returns integer
*        |    returns size of a vector
*
*
*       Access:
*
*        | method operator [] takes integer index returns $TYPE$
*        |    returns item at position index
*        |
*        | method operator []= takes integer index, $TYPE$ value returns nothing
*        |    sets item at index to value
*        |
*        | method front takes nothing returns $TYPE$
*        |    retrieves first element
*        |
*        | method back takes nothing returns $TYPE$
*        |    retrieves last element
*        |
*        | method data takes nothing returns Table
*        |    returns the underlying Table object
*
*
*       Modifiers:
*
*        | method clear takes nothing returns nothing
*        |    performs a flush operation on data table
*        |
*        | method push takes $TYPE$ value returns thistype
*        |    adds elements to the end
*        |
*        | method pop takes nothing returns thistype
*        |    removes the last element
*        |
*        | method assign takes integer count, $TYPE$ value returns thistype
*        |    assigns count elements replacing current data
*        |
*        | method insert takes integer pos, integer count, $TYPE$ value returns thistype
*        |    inserts count elements before position pos
*        |
*        | method erase takes integer pos, integer count returns thistype
*        |    erase count elements starting at position pos
*
*
*****************************************************************************/

library VectorT requires Table, Alloc

// Run here any global vector types you want to be defined.
//! runtextmacro DEFINE_VECTOR("", "IntegerVector", "integer")

//! textmacro_once DEFINE_STRUCT_VECTOR takes ACCESS, NAME, TYPE

$ACCESS$ struct $NAME$ extends array
    private delegate IntegerVector parent

    method operator[] takes integer index returns $TYPE$
        return parent[index]
    endmethod

    method front takes nothing returns $TYPE$
        return parent.front()
    endmethod

    method back takes nothing returns $TYPE$
        return parent.back()
    endmethod

    static method create takes nothing returns thistype
        local thistype this = IntegerVector.create()
        set parent = this
        return this
    endmethod
endstruct

//! endtextmacro

//! textmacro_once DEFINE_VECTOR takes ACCESS, NAME, TYPE

$ACCESS$ struct $NAME$ extends array
    private Table table
    private integer length

    implement Alloc

    private method seT takes integer index, $TYPE$ value returns nothing
        set table.$TYPE$[index] = value
    endmethod

    private method get takes integer index returns $TYPE$
        return table.$TYPE$[index]
    endmethod

    private method assert_pos takes integer pos, string f returns boolean
        debug if ( pos < 0 and pos >= length ) then
            debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::assert_pos failed at "+f+" for instance "+I2S(this)+". Invalid index at position: "+I2S(pos)+".")
        debug endif

        return ( pos >= 0 and pos < length )
    endmethod

    private method assert_range takes integer pos, string f returns boolean
        debug if ( pos < 0 or pos > length ) then
            debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::assert_range failed at "+f+" for instance "+I2S(this)+". Invalid iterator at position: "+I2S(pos)+".")
        debug endif

        return ( pos >= 0 and pos <= length )
    endmethod

    method operator [] takes integer index returns $TYPE$
        debug if not assert_pos(index, "operator []") then
            debug return get(-1)
        debug endif

        return get(index)
    endmethod

    method operator []= takes integer index, $TYPE$ value returns nothing
        debug if not assert_pos(index, "operator []=") then
            debug return
        debug endif

        call seT(index, value)
    endmethod

    static method create takes nothing returns thistype
        local thistype this = allocate()

        set table = Table.create()
        set length = 0

        return this
    endmethod

    method empty takes nothing returns boolean
        return length == 0
    endmethod

    method size takes nothing returns integer
        return length
    endmethod

    static method operator [] takes thistype vec returns thistype
        local thistype this = create()

        loop
            exitwhen length >= vec.size()
            call seT(length, vec[length])
            set length = length + 1
        endloop

        return this
    endmethod

    method clear takes nothing returns nothing
        set length = 0
        call table.flush()
    endmethod

    method destroy takes nothing returns nothing
        call clear()
        call table.destroy()
        set table = 0

        call deallocate()
    endmethod

    method front takes nothing returns $TYPE$
        return this[0]
    endmethod

    method back takes nothing returns $TYPE$
        return this[length-1]
    endmethod

    method data takes nothing returns Table
        return this
    endmethod

    method push takes $TYPE$ value returns thistype
        call seT(length, value)
        set length = length + 1

        return this
    endmethod

    method pop takes nothing returns thistype
        if ( length > 0 ) then
            set length = length - 1
            call table.$TYPE$.remove(length)
            call table.real.remove(0)
        endif

        return this
    endmethod

    method assign takes integer count, $TYPE$ value returns thistype
        if ( count > 0 ) then
            call clear()

            loop
                exitwhen length >= count
                call push(value)
            endloop
        endif

        return this
    endmethod

    method insert takes integer pos, integer count, $TYPE$ value returns thistype
        local integer i

        if assert_range(pos, "insert") then
            if ( count > 0 ) then
                set length = length + count

                set i = length - 1
                loop
                    exitwhen i < (pos + count)
                    call seT(i, get(i-count))
                    set i = i - 1
                endloop

                set i = 0
                loop
                    exitwhen i >= count
                    call seT(pos+i, value)
                    set i = i + 1
                endloop
            endif
        endif

        return this
    endmethod

    method erase takes integer pos, integer count returns thistype
        if assert_pos(pos, "erase") then
            if ( count > 0 ) then
                if ( pos + count > length ) then
                    set count = length - pos
                endif

                set pos = pos + count
                loop
                    exitwhen pos >= length
                    call seT(pos-count, get(pos))
                    set pos = pos + 1
                endloop

                loop
                    exitwhen count <= 0
                    call pop()
                    set count = count - 1
                endloop
            endif
        endif

        return this
    endmethod

endstruct

//! endtextmacro

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Alloc /* v1.3.1.1
*************************************************************************************
*
*   */
uses /*
*
*       */
optional ErrorMessage    /*      github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*       */
optional MemoryAnalysis  /*      
*
*************************************************************************************
*
*   Minimizes code generation and global variables while maintaining
*   excellent performance.
*
*       local thistype this = recycler[0]
*
*       if (recycler[this] == 0) then
*           set recycler[0] = this + 1
*       else
*           set recycler[0] = recycler[this]
*       endif
*
************************************************************************************
*
*   module Alloc
*
*       static method allocate takes nothing returns thistype
*       method deallocate takes nothing returns nothing
*
*       The Following Require Error Message To Be In The Map
*       --------------------------------------------------------
*
*           debug readonly boolean allocated
*
*       The Following Require Memory Analysis To Be In The Map
*       --------------------------------------------------------
*
*           debug readonly integer monitorCount
*           -   the amount of global memory being monitored by this
*           debug readonly integer monitorString
*           -   gets a string representation of all global memory being monitored by this
*           debug readonly integer address
*           -   global memory address for debugging
*           -   used with monitor and stopMonitor
*
*           debug static method calculateMemoryUsage takes nothing returns integer
*           debug static method getAllocatedMemoryAsString takes nothing returns string
*
*           debug method monitor takes string label, integer address returns nothing
*           -   monitor a global memory address with a label
*           -   used to identify memory leaks
*           -   should be memory that ought to be destroyed by the time this is destroyed
*           debug method stopMonitor takes integer address returns nothing
*           -   stops monitoring global memory
*           debug method stopMonitorValue takes handle monitoredHandle returns nothing
*           -   stops monitoring handle values
*
*           The Following Are Used To Monitor Handle Values
*
*               debug method monitor_widget             takes string label, widget              handleToTrack returns nothing
*               debug method monitor_destructable       takes string label, destructable        handleToTrack returns nothing
*               debug method monitor_item               takes string label, item                handleToTrack returns nothing
*               debug method monitor_unit               takes string label, unit                handleToTrack returns nothing
*               debug method monitor_timer              takes string label, timer               handleToTrack returns nothing
*               debug method monitor_trigger            takes string label, trigger             handleToTrack returns nothing
*               debug method monitor_triggercondition   takes string label, triggercondition    handleToTrack returns nothing
*               debug method monitor_triggeraction      takes string label, triggeraction       handleToTrack returns nothing
*               debug method monitor_force              takes string label, force               handleToTrack returns nothing
*               debug method monitor_group              takes string label, group               handleToTrack returns nothing
*               debug method monitor_location           takes string label, location            handleToTrack returns nothing
*               debug method monitor_rect               takes string label, rect                handleToTrack returns nothing
*               debug method monitor_boolexpr           takes string label, boolexpr            handleToTrack returns nothing
*               debug method monitor_effect             takes string label, effect              handleToTrack returns nothing
*               debug method monitor_unitpool           takes string label, unitpool            handleToTrack returns nothing
*               debug method monitor_itempool           takes string label, itempool            handleToTrack returns nothing
*               debug method monitor_quest              takes string label, quest               handleToTrack returns nothing
*               debug method monitor_defeatcondition    takes string label, defeatcondition     handleToTrack returns nothing
*               debug method monitor_timerdialog        takes string label, timerdialog         handleToTrack returns nothing
*               debug method monitor_leaderboard        takes string label, leaderboard         handleToTrack returns nothing
*               debug method monitor_multiboard         takes string label, multiboard          handleToTrack returns nothing
*               debug method monitor_multiboarditem     takes string label, multiboarditem      handleToTrack returns nothing
*               debug method monitor_dialog             takes string label, dialog              handleToTrack returns nothing
*               debug method monitor_button             takes string label, button              handleToTrack returns nothing
*               debug method monitor_texttag            takes string label, texttag             handleToTrack returns nothing
*               debug method monitor_lightning          takes string label, lightning           handleToTrack returns nothing
*               debug method monitor_image              takes string label, image               handleToTrack returns nothing
*               debug method monitor_ubersplat          takes string label, ubersplat           handleToTrack returns nothing
*               debug method monitor_region             takes string label, region              handleToTrack returns nothing
*               debug method monitor_fogmodifier        takes string label, fogmodifier         handleToTrack returns nothing
*               debug method monitor_hashtable          takes string label, hashtable           handleToTrack returns nothing
*
************************************************************************************/

    module Alloc
        /*
        *   stack
        */

        private static integer array recycler
       
        static if LIBRARY_MemoryAnalysis then
            debug private MemoryMonitor globalAddress
           
            debug method operator address takes nothing returns integer
                debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
                debug return globalAddress
            debug endmethod
        endif
       
        /*
        *   allocation
        */

        static method allocate takes nothing returns thistype
            local thistype this = recycler[0]
           
            static if LIBRARY_ErrorMessage then
                debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
            endif
           
            if (recycler[this] == 0) then
                set recycler[0] = this + 1
            else
                set recycler[0] = recycler[this]
            endif
           
            static if LIBRARY_ErrorMessage then
                debug set recycler[this] = -1
            endif
           
            static if LIBRARY_MemoryAnalysis then
                debug set globalAddress = MemoryMonitor.allocate("thistype")
            endif
           
            return this
        endmethod
       
        method deallocate takes nothing returns nothing
            static if LIBRARY_ErrorMessage then
                debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
            endif
           
            static if LIBRARY_MemoryAnalysis then
                debug call globalAddress.deallocate()
                debug set globalAddress = 0
            endif
           
            set recycler[this] = recycler[0]
            set recycler[0] = this
        endmethod
       
        static if LIBRARY_MemoryAnalysis then
            debug method monitor takes string label, integer address returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor(label, address)
            debug endmethod
            debug method stopMonitor takes integer address returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.stopMonitor(address)
            debug endmethod
            debug method stopMonitorValue takes handle monitoredHandle returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.stopMonitorValue(monitoredHandle)
            debug endmethod
           
            debug method operator monitorCount takes nothing returns integer
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
                debug return globalAddress.monitorCount
            debug endmethod
            debug method operator monitorString takes nothing returns string
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
                debug return globalAddress.monitorString
            debug endmethod
           
            debug method monitor_widget             takes string label, widget              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_widget(label, handleToTrack)
            debug endmethod
            debug method monitor_destructable       takes string label, destructable        handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_destructable(label, handleToTrack)
            debug endmethod
            debug method monitor_item               takes string label, item                handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_item(label, handleToTrack)
            debug endmethod
            debug method monitor_unit               takes string label, unit                handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_unit(label, handleToTrack)
            debug endmethod
            debug method monitor_timer              takes string label, timer               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_timer(label, handleToTrack)
            debug endmethod
            debug method monitor_trigger            takes string label, trigger             handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_trigger(label, handleToTrack)
            debug endmethod
            debug method monitor_triggercondition   takes string label, triggercondition    handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_triggercondition(label, handleToTrack)
            debug endmethod
            debug method monitor_triggeraction      takes string label, triggeraction       handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_triggeraction(label, handleToTrack)
            debug endmethod
            debug method monitor_force              takes string label, force               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_force(label, handleToTrack)
            debug endmethod
            debug method monitor_group              takes string label, group               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_group(label, handleToTrack)
            debug endmethod
            debug method monitor_location           takes string label, location            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_location(label, handleToTrack)
            debug endmethod
            debug method monitor_rect               takes string label, rect                handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_rect(label, handleToTrack)
            debug endmethod
            debug method monitor_boolexpr           takes string label, boolexpr            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_boolexpr(label, handleToTrack)
            debug endmethod
            debug method monitor_effect             takes string label, effect              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_effect(label, handleToTrack)
            debug endmethod
            debug method monitor_unitpool           takes string label, unitpool            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_unitpool(label, handleToTrack)
            debug endmethod
            debug method monitor_itempool           takes string label, itempool            handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_itempool(label, handleToTrack)
            debug endmethod
            debug method monitor_quest              takes string label, quest               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_quest(label, handleToTrack)
            debug endmethod
            debug method monitor_defeatcondition    takes string label, defeatcondition     handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
            debug endmethod
            debug method monitor_timerdialog        takes string label, timerdialog         handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_timerdialog(label, handleToTrack)
            debug endmethod
            debug method monitor_leaderboard        takes string label, leaderboard         handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_leaderboard(label, handleToTrack)
            debug endmethod
            debug method monitor_multiboard         takes string label, multiboard          handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_multiboard(label, handleToTrack)
            debug endmethod
            debug method monitor_multiboarditem     takes string label, multiboarditem      handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
            debug endmethod
            debug method monitor_dialog             takes string label, dialog              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_dialog(label, handleToTrack)
            debug endmethod
            debug method monitor_button             takes string label, button              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_button(label, handleToTrack)
            debug endmethod
            debug method monitor_texttag            takes string label, texttag             handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_texttag(label, handleToTrack)
            debug endmethod
            debug method monitor_lightning          takes string label, lightning           handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_lightning(label, handleToTrack)
            debug endmethod
            debug method monitor_image              takes string label, image               handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_image(label, handleToTrack)
            debug endmethod
            debug method monitor_ubersplat          takes string label, ubersplat           handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_ubersplat(label, handleToTrack)
            debug endmethod
            debug method monitor_region             takes string label, region              handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_region(label, handleToTrack)
            debug endmethod
            debug method monitor_fogmodifier        takes string label, fogmodifier         handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
            debug endmethod
            debug method monitor_hashtable          takes string label, hashtable           handleToTrack returns nothing
                debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
                debug call globalAddress.monitor_hashtable(label, handleToTrack)
            debug endmethod
           
            static if DEBUG_MODE then
                //! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
               
                static method calculateMemoryUsage takes nothing returns integer
                    return calculateAllocatedMemory__recycler()
                endmethod
               
                static method getAllocatedMemoryAsString takes nothing returns string
                    return allocatedMemoryString__recycler()
                endmethod
            endif
        endif
       
        /*
        *   analysis
        */

        static if LIBRARY_ErrorMessage then
            debug method operator allocated takes nothing returns boolean
                debug return recycler[this] == -1
            debug endmethod
        endif
       
        /*
        *   initialization
        */

        private static method onInit takes nothing returns nothing
            set recycler[0] = 1
        endmethod
    endmodule
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
   
    One map, one hashtable. Welcome to NewTable 4.1.0.1
   
    This newest iteration of Table introduces the new HashTable struct.
    You can now instantiate HashTables which enables the use of large
    parent and large child keys, just like a standard hashtable. Previously,
    the user would have to instantiate a Table to do this on their own which -
    while doable - is something the user should not have to do if I can add it
    to this resource myself (especially if they are inexperienced).
   
    This library was originally called NewTable so it didn't conflict with
    the API of Table by Vexorian. However, the damage is done and it's too
    late to change the library name now. To help with damage control, I
    have provided an extension library called TableBC, which bridges all
    the functionality of Vexorian's Table except for 2-D string arrays &
    the ".flush(integer)" method. I use ".flush()" to flush a child hash-
    table, because I wanted the API in NewTable to reflect the API of real
    hashtables (I thought this would be more intuitive).
   
    API
   
    ------------
    struct Table
    | static method create takes nothing returns Table
    |     create a new Table
    |    
    | method destroy takes nothing returns nothing
    |     destroy it
    |    
    | method flush takes nothing returns nothing
    |     flush all stored values inside of it
    |    
    | method remove takes integer key returns nothing
    |     remove the value at index "key"
    |    
    | method operator []= takes integer key, $TYPE$ value returns nothing
    |     assign "value" to index "key"
    |    
    | method operator [] takes integer key returns $TYPE$
    |     load the value at index "key"
    |    
    | method has takes integer key returns boolean
    |     whether or not the key was assigned
    |
    ----------------
    struct TableArray
    | static method operator [] takes integer array_size returns TableArray
    |     create a new array of Tables of size "array_size"
    |
    | method destroy takes nothing returns nothing
    |     destroy it
    |
    | method flush takes nothing returns nothing
    |     flush and destroy it
    |
    | method operator size takes nothing returns integer
    |     returns the size of the TableArray
    |
    | method operator [] takes integer key returns Table
    |     returns a Table accessible exclusively to index "key"
*/

   
globals
    private integer less = 0    //Index generation for TableArrays (below 0).
    private integer more = 8190 //Index generation for Tables.
    //Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
   
    private hashtable ht = InitHashtable()
    private key sizeK
    private key listK
endglobals
   
private struct dex extends array
    static method operator size takes nothing returns Table
        return sizeK
    endmethod
    static method operator list takes nothing returns Table
        return listK
    endmethod
endstruct
   
private struct handles extends array
    method has takes integer key returns boolean
        return HaveSavedHandle(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSavedHandle(ht, this, key)
    endmethod
endstruct
   
private struct agents extends array
    method operator []= takes integer key, agent value returns nothing
        call SaveAgentHandle(ht, this, key, value)
    endmethod
endstruct
   
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
    method operator [] takes integer key returns $TYPE$
        return Load$FUNC$(ht, this, key)
    endmethod
    method operator []= takes integer key, $TYPE$ value returns nothing
        call Save$FUNC$(ht, this, key, value)
    endmethod
    method has takes integer key returns boolean
        return HaveSaved$SUPER$(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSaved$SUPER$(ht, this, key)
    endmethod
endstruct
private module $TYPE$m
    method operator $TYPE$ takes nothing returns $TYPE$s
        return this
    endmethod
endmodule
//! endtextmacro
   
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
    method operator [] takes integer key returns $TYPE$
        return Load$FUNC$Handle(ht, this, key)
    endmethod
    method operator []= takes integer key, $TYPE$ value returns nothing
        call Save$FUNC$Handle(ht, this, key, value)
    endmethod
    method has takes integer key returns boolean
        return HaveSavedHandle(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSavedHandle(ht, this, key)
    endmethod
endstruct
private module $TYPE$m
    method operator $TYPE$ takes nothing returns $TYPE$s
        return this
    endmethod
endmodule
//! endtextmacro
   
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
   
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
   
struct Table extends array
   
    // Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
    implement realm
    implement integerm
    implement booleanm
    implement stringm
    implement playerm
    implement widgetm
    implement destructablem
    implement itemm
    implement unitm
    implement abilitym
    implement timerm
    implement triggerm
    implement triggerconditionm
    implement triggeractionm
    implement eventm
    implement forcem
    implement groupm
    implement locationm
    implement rectm
    implement boolexprm
    implement soundm
    implement effectm
    implement unitpoolm
    implement itempoolm
    implement questm
    implement questitemm
    implement defeatconditionm
    implement timerdialogm
    implement leaderboardm
    implement multiboardm
    implement multiboarditemm
    implement trackablem
    implement dialogm
    implement buttonm
    implement texttagm
    implement lightningm
    implement imagem
    implement ubersplatm
    implement regionm
    implement fogstatem
    implement fogmodifierm
    implement hashtablem
   
    method operator handle takes nothing returns handles
        return this
    endmethod
   
    method operator agent takes nothing returns agents
        return this
    endmethod
   
    //set this = tb[GetSpellAbilityId()]
    method operator [] takes integer key returns Table
        return LoadInteger(ht, this, key) //return this.integer[key]
    endmethod
   
    //set tb[389034] = 8192
    method operator []= takes integer key, Table tb returns nothing
        call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
    endmethod
   
    //set b = tb.has(2493223)
    method has takes integer key returns boolean
        return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
    endmethod
   
    //call tb.remove(294080)
    method remove takes integer key returns nothing
        call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
    endmethod
   
    //Remove all data from a Table instance
    method flush takes nothing returns nothing
        call FlushChildHashtable(ht, this)
    endmethod
   
    //local Table tb = Table.create()
    static method create takes nothing returns Table
        local Table this = dex.list[0]
       
        if this == 0 then
            set this = more + 1
            set more = this
        else
            set dex.list[0] = dex.list[this]
            call dex.list.remove(this) //Clear hashed memory
        endif
       
        debug set dex.list[this] = -1
        return this
    endmethod
   
    // Removes all data from a Table instance and recycles its index.
    //
    //     call tb.destroy()
    //
    method destroy takes nothing returns nothing
        debug if dex.list[this] != -1 then
            debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
            debug return
        debug endif
       
        call this.flush()
       
        set dex.list[this] = dex.list[0]
        set dex.list[0] = this
    endmethod
   
    //! runtextmacro optional TABLE_BC_METHODS()
endstruct
   
//! runtextmacro optional TABLE_BC_STRUCTS()
   
struct TableArray extends array
   
    //Returns a new TableArray to do your bidding. Simply use:
    //
    //    local TableArray ta = TableArray[array_size]
    //
    static method operator [] takes integer array_size returns TableArray
        local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
        local TableArray this = tb[0]         //The last-destroyed TableArray that had this array size
       
        debug if array_size <= 0 then
            debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
            debug return 0
        debug endif
       
        if this == 0 then
            set this = less - array_size
            set less = this
        else
            set tb[0] = tb[this]  //Set the last destroyed to the last-last destroyed
            call tb.remove(this)  //Clear hashed memory
        endif
       
        set dex.size[this] = array_size //This remembers the array size
        return this
    endmethod
   
    //Returns the size of the TableArray
    method operator size takes nothing returns integer
        return dex.size[this]
    endmethod
   
    //This magic method enables two-dimensional[array][syntax] for Tables,
    //similar to the two-dimensional utility provided by hashtables them-
    //selves.
    //
    //ta[integer a].unit[integer b] = unit u
    //ta[integer a][integer c] = integer d
    //
    //Inline-friendly when not running in debug mode
    //
    method operator [] takes integer key returns Table
        static if DEBUG_MODE then
            local integer i = this.size
            if i == 0 then
                call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
                return 0
            elseif key < 0 or key >= i then
                call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
                return 0
            endif
        endif
        return this + key
    endmethod
   
    //Destroys a TableArray without flushing it; I assume you call .flush()
    //if you want it flushed too. This is a public method so that you don't
    //have to loop through all TableArray indices to flush them if you don't
    //need to (ie. if you were flushing all child-keys as you used them).
    //
    method destroy takes nothing returns nothing
        local Table tb = dex.size[this.size]
       
        debug if this.size == 0 then
            debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
            debug return
        debug endif
       
        if tb == 0 then
            //Create a Table to index recycled instances with their array size
            set tb = Table.create()
            set dex.size[this.size] = tb
        endif
       
        call dex.size.remove(this) //Clear the array size from hash memory
       
        set tb[this] = tb[0]
        set tb[0] = this
    endmethod
   
    private static Table tempTable
    private static integer tempEnd
   
    //Avoids hitting the op limit
    private static method clean takes nothing returns nothing
        local Table tb = .tempTable
        local integer end = tb + 0x1000
        if end < .tempEnd then
            set .tempTable = end
            call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
        else
            set end = .tempEnd
        endif
        loop
            call tb.flush()
            set tb = tb + 1
            exitwhen tb == end
        endloop
    endmethod
   
    //Flushes the TableArray and also destroys it. Doesn't get any more
    //similar to the FlushParentHashtable native than this.
    //
    method flush takes nothing returns nothing
        debug if this.size == 0 then
            debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
            debug return
        debug endif
        set .tempTable = this
        set .tempEnd = this + this.size
        call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
        call this.destroy()
    endmethod
   
endstruct
   
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array

    //Enables myHash[parentKey][childKey] syntax.
    //Basically, it creates a Table in the place of the parent key if
    //it didn't already get created earlier.
    method operator [] takes integer index returns Table
        local Table t = Table(this)[index]
        if t == 0 then
            set t = Table.create()
            set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
        endif
        return t
    endmethod

    //You need to call this on each parent key that you used if you
    //intend to destroy the HashTable or simply no longer need that key.
    method remove takes integer index returns nothing
        local Table t = Table(this)[index]
        if t != 0 then
            call t.destroy()
            call Table(this).remove(index)
        endif
    endmethod
   
    //Added in version 4.1
    method has takes integer index returns boolean
        return Table(this).has(index)
    endmethod
   
    //HashTables are just fancy Table indices.
    method destroy takes nothing returns nothing
        call Table(this).destroy()
    endmethod
   
    //Like I said above...
    static method create takes nothing returns thistype
        return Table.create()
    endmethod

endstruct

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//*  To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x)   : Get a timer (alternative to CreateTimer), call
//*                            Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2)     : Attach value 2 to timer
//* GetTimerData(t)       : Get the timer's value.
//*                         You can assume a timer's value is 0
//*                         after NewTimer.
//*
//* Multi-flavor:
//*    Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************

//================================================================
    globals
        //How to tweak timer utils:
        // USE_HASH_TABLE = true  (new blue)
        //  * SAFEST
        //  * SLOWEST (though hash tables are kind of fast)
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true  (orange)
        //  * kinda safe (except there is a limit in the number of timers)
        //  * ALMOST FAST
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
        //  * THE FASTEST (though is only  faster than the previous method
        //                  after using the optimizer on the map)
        //  * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
        //                     work)
        //
        private constant boolean USE_HASH_TABLE      = true
        private constant boolean USE_FLEXIBLE_OFFSET = false

        private constant integer OFFSET     = 0x100000
        private          integer VOFFSET    = OFFSET
             
        //Timers to preload at map init:
        private constant integer QUANTITY   = 256
       
        //Changing this  to something big will allow you to keep recycling
        // timers even when there are already AN INCREDIBLE AMOUNT of timers in
        // the stack. But it will make things far slower so that's probably a bad idea...
        private constant integer ARRAY_SIZE = 8190

    endglobals

    //==================================================================================================
    globals
        private integer array data[ARRAY_SIZE]
        private hashtable     ht
    endglobals
   
   

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        static if(USE_HASH_TABLE) then
            // new blue
            call SaveInteger(ht,0,GetHandleId(t), value)
           
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-VOFFSET]=value
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-OFFSET]=value
        endif        
    endfunction

    function GetTimerData takes timer t returns integer
        static if(USE_HASH_TABLE) then
            // new blue
            return LoadInteger(ht,0,GetHandleId(t) )
           
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-VOFFSET]
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-OFFSET]
        endif        
    endfunction

    //==========================================================================================
    globals
        private timer array tT[ARRAY_SIZE]
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
       
        private boolean       didinit = false
    endglobals
    private keyword init

    //==========================================================================================
    // I needed to decide between duplicating code ignoring the "Once and only once" rule
    // and using the ugly textmacros. I guess textmacros won.
    //
    //! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
    // On second thought, no.
    //! endtextmacro

    function NewTimerEx takes integer value returns timer
        if (tN==0) then
            if (not didinit) then
                //This extra if shouldn't represent a major performance drawback
                //because QUANTITY rule is not supposed to be broken every day.
                call init.evaluate()
                set tN = tN - 1
            else
                //If this happens then the QUANTITY rule has already been broken, try to fix the
                // issue, else fail.
                debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
                set tT[0]=CreateTimer()
                static if( not USE_HASH_TABLE) then
                    debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
                    static if( USE_FLEXIBLE_OFFSET) then
                        if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    else
                        if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    endif
                endif
            endif
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],value)
     return tT[tN]
    endfunction
   
    function NewTimer takes nothing returns timer
        return NewTimerEx(0)
    endfunction


    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        if(t==null) then
            debug call BJDebugMsg("Warning: attempt to release a null timer")
            return
        endif
        if (tN==ARRAY_SIZE) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            call PauseTimer(t)
            if(GetTimerData(t)==HELD) then
                debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                return
            endif
            call SetTimerData(t,HELD)
            set tT[tN]=t
            set tN=tN+1
        endif    
    endfunction

    private function init takes nothing returns nothing
     local integer i=0
     local integer o=-1
     local boolean oops = false
        if ( didinit ) then
            return
        else
            set didinit = true
        endif
     
        static if( USE_HASH_TABLE ) then
            set ht = InitHashtable()
            loop
                exitwhen(i==QUANTITY)
                set tT[i]=CreateTimer()
                call SetTimerData(tT[i], HELD)
                set i=i+1
            endloop
            set tN = QUANTITY
        else
            loop
                set i=0
                loop
                    exitwhen (i==QUANTITY)
                    set tT[i] = CreateTimer()
                    if(i==0) then
                        set VOFFSET = GetHandleId(tT[i])
                        static if(USE_FLEXIBLE_OFFSET) then
                            set o=VOFFSET
                        else
                            set o=OFFSET
                        endif
                    endif
                    if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
                        exitwhen true
                    endif
                    if (GetHandleId(tT[i])-o>=0)  then
                        set i=i+1
                    endif
                endloop
                set tN = i
                exitwhen(tN == QUANTITY)
                set oops = true
                exitwhen not USE_FLEXIBLE_OFFSET
                debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")              
            endloop
           
            if(oops) then
                static if ( USE_FLEXIBLE_OFFSET) then
                    debug call BJDebugMsg("The problem has been fixed.")
                    //If this message doesn't appear then there is so much
                    //handle id fragmentation that it was impossible to preload
                    //so many timers and the thread crashed! Therefore this
                    //debug message is useful.
                elseif(DEBUG_MODE) then
                    call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
                    call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
                    call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
                    call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
                endif
            endif
        endif

    endfunction

endlibrary
//TESH.scrollpos=4
//TESH.alwaysfold=0
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
*   v1.2.1, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   UnitDex assigns every unit an unique integer. It attempts to make that number within the
*   maximum array limit so you can associate it with one.
*   _________________________________________________________________________
*   1. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Copy the script to your map, save it, then restart the editor and comment out the line below.
*/

    ///! external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/*  ________________________________________________________________________
*   2. Configuration
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
 
    private module UnitDexConfig
   
        // The raw code of the leave detection ability.
        static constant integer DETECT_LEAVE_ABILITY = 'uDex'
       
        // Allow debug messages (debug mode must also be on)
        static constant boolean ALLOW_DEBUGGING      = true
       
        // Uncomment the lines below to define a filter for units being indexed
       
        /*static method onFilter takes unit u returns boolean
            return true
        endmethod*/

       
    endmodule
/*  _________________________________________________________________________
*   3. Function API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Every function inlines except for UnitDexRemove
*
*       function GetUnitId takes unit whichUnit returns integer
*       function GetUnitById takes integer index returns unit
*    
*       function UnitDexEnable takes boolean flag returns nothing
*       function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
*       function IsUnitIndexed takes unit u returns boolean
*       function IsIndexingEnabled takes nothing returns boolean
*
*       function GetIndexedUnit takes nothing returns unit
*       function GetIndexedUnitId takes nothing returns integer
*      
*       function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
*       function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
*       function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
*       function OnUnitIndex takes code func returns triggercondition
*       function OnUnitDeidex takes code func returns triggercondition
*   _________________________________________________________________________
*   4. Struct API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       UnitDex.Enabled = false // toggle the indexer
*       UnitDex.Initialized     // returns true if the preload timer has finished
*       UnitDex.Count           // returns the amount of units indexed
*       UnitDex.Unit[0]         // access the UnitDex array directly
*       UnitDex.Group           // a unit group containing every indexed unit (for enumeration)
*       UnitDex.LastIndex       // returns the last indexed unit's id
*   _________________________________________________________________________
*   5. Public Variables
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       These are to be used with the "eventtype" argument of the event API:
*
*           constant integer EVENT_UNIT_INDEX     = 0
*           constant integer EVENT_UNIT_DEINDEX   = 1
*   _________________________________________________________________________
*   6. Examples
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Associate a unit with a variable
*
*           set UnitFlag[GetUnitId(yourUnit)] = true
*
*       2. Allocate a struct instance using a units assigned ID
*
*           local somestruct data = GetUnitId(yourUnit)
*  
*       3. Detect when a unit leaves the map
*
*           function Exit takes nothing returns nothing
*               call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
*           endfunction
*
*           call OnUnitDeindex(function Exit)
*           // or
*           call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
*   _________________________________________________________________________
*   7. How it works
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Enumerate all preplaced units
*       2. TriggerRegisterEnterRegion native to detect when a unit enters the map
*       3. Assign each unit that enters the map a unique integer
*       4. Give every unit an ability based off of defend. There is no native to accurately
*          detect when a unit leaves the map but when a unit dies or is removed from the game
*          they are issued the "undefend" order
*       5. Catch the "undefend" order and remove unit from the queue
*   _________________________________________________________________________
*   8. Notes
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
*       - The object merger line should be commented out after saving and restarting.
*       - All public functions are inlined except UnitDexRemove.
*
*   -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/

   
    globals
        // Event types
        constant integer EVENT_UNIT_INDEX     = 0
        constant integer EVENT_UNIT_DEINDEX   = 1
       
        // System variables
        private trigger array IndexTrig
        private integer Index = 0
        private real E=-1
        private boolexpr FilterEnter
    endglobals
   
    function GetUnitId takes unit whichUnit returns integer
        return GetUnitUserData(whichUnit)
    endfunction
   
    function GetUnitById takes integer index returns unit
        return UnitDex.Unit[index]
    endfunction
   
    function GetIndexedUnit takes nothing returns unit
        return UnitDex.Unit[UnitDex.LastIndex]
    endfunction
   
    function GetIndexedUnitId takes nothing returns integer
        return UnitDex.LastIndex
    endfunction
   
    function IsUnitIndexed takes unit u returns boolean
        return (GetUnitById(GetUnitId(u)) != null)
    endfunction
   
    function UnitDexEnable takes boolean flag returns nothing
        set UnitDex.Enabled = flag
    endfunction
   
    function IsIndexingEnabled takes nothing returns boolean
        return UnitDex.Enabled
    endfunction
   
    function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
        return TriggerAddCondition(IndexTrig[eventtype], func)
    endfunction
   
    function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
        call TriggerRemoveCondition(IndexTrig[eventtype], c)
    endfunction
   
    function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
        call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
    endfunction
   
    function OnUnitIndex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
    endfunction

    function OnUnitDeindex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
    endfunction
   
    function UnitDexRemove takes unit u, boolean runEvents returns boolean
        return UnitDex.Remove(u, runEvents)
    endfunction
   
    /****************************************************************/
   
    private keyword UnitDexCore
   
    struct UnitDex extends array
        static boolean Enabled = true
       
        readonly static integer LastIndex
        readonly static boolean Initialized=false
        readonly static group Group=CreateGroup()
        readonly static unit array Unit
        readonly static integer Count = 0
        readonly static integer array List
       
        implement UnitDexConfig
       
        private static integer Counter = 0
       
        implement UnitDexCore
    endstruct
   
    /****************************************************************/
     
    private module UnitDexCore
   
        static method Remove takes unit u, boolean runEvents returns boolean
            local integer i
           
            if (IsUnitIndexed(u)) then
                set i = GetUnitId(u)
                set UnitDex.List[i] = Index
                set Index = i
               
                call GroupRemoveUnit(UnitDex.Group, u)
                call SetUnitUserData(u, 0)
           
                if (runEvents) then
                    set UnitDex.LastIndex = i
                    set E = EVENT_UNIT_DEINDEX
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                    set E = -1
                endif
               
                set UnitDex.Unit[i] = null
                set UnitDex.Count = UnitDex.Count - 1
               
                return true
            endif
           
            return false
        endmethod
       
        private static method onGameStart takes nothing returns nothing
            local integer i = 0
            static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
                local group ENUM_GROUP = CreateGroup() // If not, create the group.
            endif
           
            // Index preplaced units
            loop
                call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
               
                set i = i + 1
               
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
           
            static if (not LIBRARY_GroupUtils) then
                call DestroyGroup(ENUM_GROUP)
                set ENUM_GROUP = null
            endif
           
            // run init triggers
            set i = 1
            loop
                exitwhen i > Counter
               
                set LastIndex = i
               
                call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                set E = EVENT_UNIT_INDEX
                set E = -1
               
                set i = i + 1
            endloop

            set LastIndex   = Counter
            set Initialized = true
            set FilterEnter = null
           
            call DestroyTimer(GetExpiredTimer())
        endmethod
       
        private static method onEnter takes nothing returns boolean
            local unit    u = GetFilterUnit()
            local integer i = GetUnitId(u)
            local integer t = Index
           
            if (i == 0 and thistype.Enabled) then
               
                // If a filter was defined pass the unit through it.
                static if (thistype.onFilter.exists) then
                    if (not thistype.onFilter(u)) then
                        set u = null
                        return false // check failed
                    endif
                endif
               
                // Handle debugging
                static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
                    if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
                        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
                        set u = null
                        return false
                    endif
                endif
               
                // Add to group of indexed units
                call GroupAddUnit(thistype.Group, u)
               
                // Give unit the leave detection ability
                call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
                call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
               
                // Allocate index
                if (Index != 0) then
                    set Index = List[t]
                else
                    set Counter = Counter + 1
                    set t = Counter
                endif
               
                set List[t] = -1
                set LastIndex = t
                set thistype.Unit[t] = u
                set Count = Count + 1
               
                // Store the index
                call SetUnitUserData(u, t)
               
                if (thistype.Initialized) then
                    // Execute custom events registered with RegisterUnitIndexEvent
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                   
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_INDEX

                    // Reset so the event can occur again
                    set E = -1
                endif
            endif
           
            set u = null
           
            return false
        endmethod

        private static method onLeave takes nothing returns boolean
            local unit    u
            local integer i
           
            // Check if order is undefend.
            if (thistype.Enabled and GetIssuedOrderId() == 852056) then
               
                set u = GetTriggerUnit()
               
                // If unit was killed (not removed) then don't continue
                if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
                    set u = null
                    return false
                endif
               
                set i = GetUnitId(u)

                // If unit has been indexed then deindex it
                if (i > 0 and i <= Counter and u == GetUnitById(i)) then
                   
                    // Recycle the index
                    set List[i]   = Index
                    set Index     = i
                    set LastIndex = i
                   
                    // Remove to group of indexed units
                    call GroupRemoveUnit(thistype.Group, u)
               
                    // Execute custom events without any associated triggers
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                   
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_DEINDEX
                   
                    // Remove entry
                    call SetUnitUserData(u, 0)
                    set thistype.Unit[i] = null
                   
                    // Decrement unit count
                    set Count = Count - 1
               
                    // Reset so the event can occur again
                    set E = -1
                endif
               
                set u = null
            endif
           
            return false
        endmethod
       
        private static method onInit takes nothing returns nothing
            local trigger t         = CreateTrigger()
            local integer i         = 0
            local player p
            local unit u
           
            static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
                local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
                local rect world = GetWorldBounds()
            endif
           
            set FilterEnter = Filter(function thistype.onEnter)
           
            // Begin to index units when they enter the map
            static if (LIBRARY_WorldBounds) then
                call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
            else
                call RegionAddRect(reg, world)
                call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
                call RemoveRect(world)
                set world = null
            endif
           
            call TriggerAddCondition(t, Filter(function thistype.onLeave))
           
            set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
            set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
           
            loop
                set p = Player(i)
               
                // Detect "undefend"
                call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
               
                // Hide the detect ability from players
                call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
               
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
           
            call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
        endmethod
   
    endmodule
   
endlibrary